遍历器(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...of
Array.from
Map()
、Set()
、WeakMap()
、WeakSet()
如(new Map([['a', 1], ['b', 2]])
)Promise.all()
Promise.race()
以上是关于数组的遍历器。