遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构(数组、Set、Map等)提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)供 for…of消费。
for…of遍历机制:
- 会寻找被遍历对象是否含有Symbol.iterator方法,如果没有报错
- 当for…of遍历的时候,会调用遍历器对象的next方法
- 当done为false的时候继续遍历,为true的时候,停止遍历
想要被for…of 遍历:
- 部署Symbol.iterator方法,返回迭代器对象iterator object
- 迭代器对象中提供next方法被for…of遍历
- next方法返回 {value: any,done:Boolean}
原生具备 Iterator 接口的数据结构如下。
- Array
- Map
- Set
- String
- TypedArray
- 函数的 arguments 对象
- NodeList 对象
对象(Object)之所以没有默认部署 Iterator 接口,是因为对象的哪个属性先遍历,哪个属性后遍历是不确定的,需要开发者手动指定。本质上,遍历器是一种线性处理,对于任何非线性的数据结构,部署遍历器接口,就等于部署一种线性转换
部署了遍历器的数据结构我们称为是可迭代的(iterable),也就是可以:
- for..of
- rest、spread 扩展运算符
- yield*
- Array.from()
举例:
Object对象正常是不可迭代的
要想range可迭代,我们按照以上步骤来实现下:
let range = {
from: 1,
to: 5,
};
// 1. for..of 调用首先会调用这个:
range[Symbol.iterator] = function () {
// ……它返回迭代器对象(iterator object):
// 2. 接下来,for..of 仅与此迭代器一起工作,要求它提供下一个值
return {
current: this.from,
last: this.to,
// 3. next() 在 for..of 的每一轮循环迭代中被调用
next() {
// 4. 它将会返回 {done:.., value :...} 格式的对象
if (this.current <= this.last) {
return { value: this.current++, done: false };
} else {
return { value: undefined, done: true };
}
},
};
};
for (let r of range) {
console.log(r);
}
字符串原生就部署了遍历器
const str = 'helllo';
const iterator = str[Symbol.iterator]();
iterator.next(); //{value: 'h', done: false}
iterator.next(); //{value: 'h', done: false}
iterator.next(); //{value: 'h', done: false}
...
iterator与generator
let range = {
from: 1,
to: 5,
*[Symbol.iterator]() { // [Symbol.iterator]: function*() 的简写形式
for(let value = this.from; value <= this.to; value++) {
yield value;
}
}
};