一、 简述
js中有四种表示“集合”的数据结构:Array、Object、Map、Set,此外还能组合使用定义自己的数据结构,Iterator应运而生,来处理不同的数据结构。
遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
1.2 Iterator 的作用
- 为各种数据结构,提供一个统一的、简便的访问接口
- 使得数据结构的成员能够按某种次序排列
- ES6 创造了一种新的遍历命令
for...of循环,Iterator 接口主要供for...of消费
1.3 Iterator 的遍历过程
- 创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象
- 第一次调用指针对象的
next方法,可以将指针指向数据结构的第一个成员(返回{value:当前值,done:false}) - 第二次调用指针对象的
next方法,指针就指向数据结构的第二个成员{value:当前值,done:false} - 不断调用指针对象的
next方法,直到它指向数据结构的结束位置{value:undefined,done:true}
二、 默认Iterator接口
ES6 规定,默认的 Iterator 接口部署在数据结构的
Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。至于属性名Symbol.iterator,它是一个表达式,返回Symbol对象的iterator属性,这是一个预定义好的、类型为 Symbol 的特殊值,所以要放在方括号内
const obj = {
[Symbol.iterator] : function () {
return {
next: function () {
return {
value: 1,
done: true
};
}
};
}
};
2.1 对象没有默认部署 Iterator 接口
因为对象的哪个属性先遍历,哪个属性后遍历是不确定的,需要开发者手动指定。
一个对象如果要具备可被for...of循环调用的 Iterator 接口,就必须在Symbol.iterator的属性上部署遍历器生成方法(原型链上的对象具有该方法也可)
class RangeIterator {
constructor(start, stop) {
this.value = start;
this.stop = stop;
}
[Symbol.iterator]() { return this; }
next() {
var value = this.value;
if (value < this.stop) {
this.value++;
return {done: false, value: value};
}
return {done: true, value: undefined};
}
}
function range(start, stop) {
return new RangeIterator(start, stop);
}
for (var value of range(0, 3)) {
console.log(value); // 0, 1, 2
}
三、调用Iterator的场合
3.1 解构赋值
3.2 扩展运算符
3.3 yield *
3.4 其他
由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器接口。下面是一些例子。
-
for...of
-
Array.from()
-
Map(), Set(), WeakMap(), WeakSet()(比如
new Map([['a',1],['b',2]])) -
Promise.all()
-
Promise.race()
四、字符串的Iterator接口
字符串是一个类似数组的对象,也原生具有 Iterator 接口。
var someString = "hi";
typeof someString[Symbol.iterator]
// "function"
var iterator = someString[Symbol.iterator]();
iterator.next() // { value: "h", done: false }
iterator.next() // { value: "i", done: false }
iterator.next() // { value: undefined, done: true }
4.1 修改默认的遍历器接口
可以覆盖原生的Symbol.iterator方法,达到修改遍历器行为的目的。
var str = new String("hi");
[...str] // ["h", "i"]
str[Symbol.iterator] = function() {
return {
next: function() {
if (this._first) {
this._first = false;
return { value: "bye", done: false };
} else {
return { done: true };
}
},
_first: true
};
};
[...str] // ["bye"]
str // "hi"
字符串 str 的Symbol.iterator方法被修改了,所以扩展运算符(...)返回的值变成了bye,而字符串本身还是hi