Iterator
遍历器,是一种接口,为不同的数据结构提供一个统一的访问机制,任何数据结构只要部署了Iterator接口,就可以完成遍历操作。
ES6中创造了一种新的遍历命令for...of
,而Iterator就是供for...of
操作。
Iterator 的遍历过程:
- 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
- 第一次调用指针对象的
next
方法,可以将指针指向数据结构的第一个成员。 - 第二次调用指针对象的
next
方法,指针就指向数据结构的第二个成员。 - 不断调用指针对象的
next
方法,直到它指向数据结构的结束位置。
调用next方法,会返回一个包含value和done这两个属性的对象。value为当前属性的值,done是一个Boolean值,表示遍历是否结束了。
一种数据结构只要部署了iterator结构,那么这种数据结构就是可遍历的iterable
。
在ES6中。默认Iterator接口部署在数据结构的symbol.iterator
属性,该属性是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数就会返回一个遍历器。
const obj = {
[Symbol.iterator] : function () {
return {
next: function () {
return {
value: 1,
done: true
};
}
};
}
};
原生具备 Iterator 接口的数据结构有:
- Array
- Map
- Set
- String
- TypedArray
- 函数的 arguments 对象
- NodeList 对象
其他数据结构(主要是对象)的 Iterator 接口,都需要在Symbol.iterator
属性上面部署,这样才会被for...of
循环遍历。
对于类似数组的对象(存在数值键名和length
属性),部署 Iterator 接口,有一个简便方法,就是Symbol.iterator
方法直接引用数组的 Iterator 接口。
NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
// 或者
NodeList.prototype[Symbol.iterator] = [][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'
}
普通对象部署数组的`Symbol.iterator`方法不起作用
let iterable = {
a: 'a',
b: 'b',
c: 'c',
length: 3,
[Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
console.log(item); // undefined, undefined, undefined
}
如果Symbol.iterator
方法对应的不是遍历器生成函数(即会返回一个遍历器对象),解释引擎将会报错。
var obj = {};
obj[Symbol.iterator] = () => 1;
[...obj] // TypeError: [] is not a function 扩展运算符也是采用了遍历器
有了遍历器接口,数据结构就可以用for...of
循环遍历,也可以使用while
循环遍历。
var $iterator = ITERABLE[Symbol.iterator]();
var $result = $iterator.next();
while (!$result.done) {
var x = $result.value;
// ...
$result = $iterator.next();
}
使用了iterator接口的场合
- 解构赋值,对数组和 Set 结构进行解构赋值时,会默认调用
Symbol.iterator
方法。 - 扩展运算符,(…)也会调用默认的 Iterator 接口。
- for…of
- Map(),Set(),WeakMap(),WeakSet()