遍历器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据只要部署Iterator接口,就可以完成遍历操作。
作用有三:
- 为各种数据提供统一的访问接口。
- 使得数据结构的成员能够按某种次序排列。
- ES6创造了新的遍历命令
for...of循环,提供遍历器使用。
换句话说,如果使用了for...of循环遍历某种数据结构时,该循环就会去寻找 Iterator 接口。只要某个数据结构部署了 Iterator接口,就称这种数据结构为“可遍历的(iterable)”
而默认的Iterator接口,部署在数据结构的Symbol.iterator属性,一个数据只要有这个属性,就可以认为是“可遍历的”。Symbol.iterater属性其实是个函数,执行这个函数,就会返回一个遍历器。而属性名Symbol.iterater 看得出来,是Symbol对象的iterator属性,类型为Symbol的特殊值,所以放在放括号里!(忘记可回看Symbol知识)
由Symbol.iterater返回的函数,这个函数的执行过程是这样的:
- 创建一个指针对象,指向当前的起始位置。
- 第一次调用指针对象的
next方法,指向数据结构的第一个成员。 - 第二次同样的操作,只不过是指向数据结构第二个成员。
- 不断的调用
next方法,直到指向结束位置。
同时,每一次调用next方法,都会返回数据结构的当前成员的信息,其中返回的是value与done两个属性的对象。value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
以上根据上面的执行过程来模拟遍历器代码:
function makeIterator(array){let nextIndex = 0;return {next: function(){return nextIndex < array.length ?{value: array[nextIndex++], done: false} :{value: undefined, done: true};}}}let it = makeIterator(['a', 'b'])it.next() //{ value: "a", done: false }it.next() //{ value: "b", done: false }it.next() //{ value: undefined, done: true}
由于Iterator只是把接口规格加到数据结构上,所以,遍历器和要遍历的那个数据结构,实际上是分开的。
当然,ES6有些数据原生具备了Iterator接口,如下:
- Array
- Map
- Set
- String
- TypedArray
- 函数的arguments对象
- NodeList对象
例如:
let arr = [1,2,3]let iter = arr[Symbol.iterator]()iter.next() //{ value: 1, done: false }iter.next() //{ value: 2, done: false }iter.next() //{ value: 3, done: false }iter.next() //{ value: undefined, done: true }
因为原生就自带,所以可以直接获取到Iterator的方法!
而Object却没有这样的遍历器,因为Object需要哪个先执行,哪个个后执行,是不确定的,需要开发者自己动手去设置。不过也没什么必要,要真部署的话就和Map没什么区别的了。
部署过程分两种,第一种使用class部署,第二种使用了原型链上部署。具体部署过程就不细说了!
调用Iterator接口的场合
有些场合会默认调用 Iterator接口(即Symbol.iterator方法),除了for...of会调用外,还有:
- 解构赋值:对数组和Set结构进行解构赋值时,会默认调用
Symbol.iterator方法。 - 扩展运算符:实际上,只要任何部署了
Symbol.iterator,都可以使用扩展运算符。 yield*:yield*后面跟的是一个可遍历的结构,他会调用该结构的遍历器接口。
同时,因为数组的遍历会调用遍历接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口。如下例子:
for...ofArray.fromMap()、Set()、WeakMap()、WeakSet()如(new Map([['a', 1], ['b', 2]]))Promise.all()Promise.race()
以上是关于数组的遍历器。
