Iterator(遍历器)
概念
- JavaScript原有的表示"集合"的数据结构,主要是数组和对象,ES6又添加了Map和Set.用户可以组合使用它们,定义自己的数据结构.这样就需要统一的接口机制处理所有不同的数据结构.Iterator就是这样一种机制,它是一种接口,为各种不同的数据结构提供统一的访问机制.任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)
作用
- 为各种数据结构提供统一的,简便的访问接口
- 使数据结构的成员能够按照某种次序排列
- ES6创造了一种新的遍历命令for…of循环,Iterator主要供其使用.也就是当使用for…of时,该循环会自动去寻找Iterator接口
Iterator的遍历过程:
- 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象
- 第一次调用指针对象的 next 方法,可以将指针指向数据结构的第一个成员
- 第二次调用指针对象的 next 方法,指针就指向数据结构的第二个成员
- 不断调用对象的next方法,直到它指向数据结构的结束位置
- 每一次调用next方法都会返回数据结构的当前成员的信息.具体来说就是返回一个包含value和done两个属性的对象.value是当前成员的值,done属性是一个布尔值,表示遍历是否结束
var it=makeIterator(['a','b']);
it.next(); //{value:'a',done:false}
it.next(); //{value:'b',done:false}
it.next(); //{value:undefined,done:true}
function makeIterator(arr){
var nextIndex=0;
return {
next : function(){
return nextIndex < arr.length-1 ?
{value:arr[nextIndex++],done:false} : {value:undefined,done:true};
}
}
}
//这里的arr[nextIndex++]执行顺序是执行arr[nextIndex]; nextIndex++;
默认Iterator接口
- ES6规定,默认的Iterator接口部署在数据结构的Symbol.iterator属性,或者说一个数据结构只要有Symbol.iterator属性就可以认为是可遍历的.Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数
- ES6的有些数据结构原生具备Iterator接口,比如数组,即不用任何处理就能被for…of循环遍历.对象没有Iterator接口
- 原生具备Iterator接口的数据结构:Array,Map,Set,String,TypedArray,NodeList对象,函数的arguments对象
- 对于类似数组的对象(存在数值键名和length属性),部署Iterator接口,可以直接使其Symbol.iterator方法引用数组的Iterator接口.比如NodeList:
NodeList.prototype[Symbol.iterator]=Array.prototype[Symbol.iterator].NodeList
- 注意:普通对象部署数组的Symbol.iterator方法没有效果.因为普通对象的键值是字符串,不是数值键名
- 有了遍历器接口,数据结构就可以用for…of循环遍历,也可以使用while循环遍历
调用iterator接口的场合
- 解构赋值:对数组和Set结构进行解构赋值时,会默认调用Symbol.iterator方法
- 扩展运算符(…)内部会调用Iterator接口.只要某个数据结构部署了Iterator接口,就可以对它使用扩展运算符,将其转为数组
- yield*后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口
let generator = function* () {
yield 1;
yield* [2,3,4];
yield 5;
};
var iterator = generator();
iterator.next() // { value: 1, done: false }
iterator.next() // { value: 2, done: false }
iterator.next() // { value: 3, done: false }
iterator.next() // { value: 4, done: false }
iterator.next() // { value: 5, done: false }
iterator.next() // { value: undefined, done: true }
- 其他场合:由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口
遍历器对象的return(),throw()
- 遍历器除了具有next方法,还可以具有return方法和throw方法.后两者是可选的
- return()方法的使用场合是,如果for…of循环提前退出(通常是因为出错,或者有break和continue语句),就会调用return方法.如果一个对象在完成遍历前,需要清理或释放资源,就可以部署return方法
- throw方法主要是配合generator函数使用,一般的遍历器对象用不到