1、遍历器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
2、Iterator的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是ES6创造了一种新的遍历命令for...of
循环,Iterator接口主要供for...of
消费。
3、在ES6中,有些数据结构原生具备Iterator接口(比如数组),即不用任何处理,就可以被for...of
循环遍历,有些就不行(比如对象)。原因在于,这些数据结构原生部署了Symbol.iterator
属性(详见下文),另外一些数据结构没有。凡是部署了Symbol.iterator
属性的数据结构,就称为部署了遍历器接口。调用这个接口,就会返回一个遍历器对象。
4、在ES6中,有三类数据结构原生具备Iterator接口:数组、某些类似数组的对象、Set和Map结构。还有函数的 arguments 对象、 NodeList 对象。
5、实现本质
(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
(2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
(3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
(4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。
每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束
6、一个为对象添加Iterator接口的例子。
let obj = {
data: [ 'hello', 'world' ],
[Symbol.iterator]() {
const self = this;
let index = 0;
return {
next() {
if (index < self.data.length) {
return {
value: self.data[index++],
done: false
};
} else {
return { value: undefined, done: true };
}
}
};
}
};
7、下面是类似数组的对象调用数组的Symbol.iterator
方法的例子。
let iterable = { 0: 'a', 1: 'b', 2: 'c', length: 3, [Symbol.iterator]: Array.prototype[Symbol.iterator] }; for (let item of iterable) { console.log(item); // 'a', 'b', 'c' }
Array.prototype[Symbol.iterator]这块就是在调用数组的symbol.iterator方法。
注意,普通对象部署数组的Symbol.iterator
方法,并无效果。
8、有一些场合会默认调用Iterator接口(即Symbol.iterator
方法),除了下文会介绍的for...of
循环,还有几个别的场合。
- 解构赋值
- 扩展运算符(...)
- yield*_yield*后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。
- 由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用
9、字符串是一个类似数组的对象,也原生具有Iterator接口。
10、遍历器对象除了具有next
方法,还可以具有return
方法和throw
方法。如果你自己写遍历器对象生成函数,那么next
方法是必须部署的,return
方法和throw
方法是否部署是可选的。