简单谈谈
Iterator
(遍历器)是一种机制:当外部访问所属对象时,提供统一的访问机制。任何的数据结构一旦部署了 Iterator
接口,就相当于实现了统一的遍历机制,使得外部程序在使用这些不同的数据结构时,有一致的写法。
具体到语言层面,需要实现了以下内容:
- 对象下需要部署
Symbol.iterator
方法。 - 该方法需要返回一个含有
next
方法的对象:{ next: () => { value: any, done: boolean }
}`。 - 该对象的
next
方法需要有统一的返回结构:{ value: any, done: boolean }
。
部署了上述特性的对象,就叫做:可遍历对象。在 ES6
中,Map
、Set
、Array
、String
等的实例都具备上述结构。但并非仅有这些类的实例时可遍历对象,只要是对象有上述特性,就是可遍历对象,因此可遍历对象是可以由我们自己创建的。
Iterator 接口
ES6
规定,默认的 Iterator
接口部署在对象的 Symbol.iterator
属性上,当该属性有值时,该对象即是可遍历对象。
一个简单的可遍历对象:
const obj = {
[Symbol.iterator]: function () {
return {
next: function () {
return {
value: 1,
done: true
}
}
}
}
}
obj
对象下部署了 Symbol.iterator
方法,那么该对象即是可遍历对象,并如上述所说,执行该方法会获得一个具有 next
属性的对象,调用返回对象的 next
方法,即可获得 {value: any, done: boolean}
结构的数据。
常见原生就具备 Iterator
接口的对象:
Array
Map
Set
String
- 函数的
arguments
对象 NodeList
对象
可以用以下方法确定某个对象是否具备 Iterator
接口。
let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();
iter.next(); // { value: 'a', done: false }
iter.next(); // { value: 'b', done: false }
iter.next(); // { value: 'c', done: false }
iter.next(); // { value: undefined, done: true }
for...of
如果一个对象是可遍历对象,那么该对象就可以被 for...of
循环调用,反过来说就是,如果一个对象可被 for...of
循环,那么该对象一定拥有 Symbol.iterator
方法,并且该方法能返回符合要求的结果。
对数组调用 for...of
循环。
let arr = ['a', 'b', 'c']
for(let value of arr){
console.log(value) // a b c
}
实现自定义的 Iterator
接口,并调用 for...of
循环。
class RangeIterator {
constructor(start, stop) {
this.value = start;
this.stop = stop;
}
[Symbol.iterator]() {
return this
}
next() {
let value = this.value;
if (value < this.stop) {
this.value++
return {
done: false,
value
}
}
return {
done: true,
value: undefined
}
}
}
for (let value of new RangeIterator(0, 3)) {
console.log(value)
// 0, 1, 2
}
遍历过程
当有方法或者动作需要进行遍历对象的时候(触发 Symbol.iterator
),会进行以下步骤:
- 调用
Symbol.iterator
方法,获得next
函数。 - 调用
next
方法,获得返回值。 - 判断返回值中的
done
属性,若为true
则停止循环。 - 获得返回值中的
value
属性并操作,操作完成后回到步骤2
。
调用场合
解构赋值
let set = new Set().add('a').add('b').add('c');
let [x, y] = set; // x = 'a'; y = 'b'
let [first, ...rest] = set; // first = 'a'; rest = ['b', 'c']
使用扩展运算符
let str = 'hello';
[...str]; // ['h','e','l','l','o']
let arr = ['b', 'c'];
['a', ...arr, 'd']; // ['a', 'b', 'c', 'd']
for...of
let array = [1, 2, 3];
for (let vaue of array) {
// do something
}
Map & Set
let set = new Set([1, 2, 3, 4]);
let map = new Map([['a', 1], ['b', 2]]);
Array.from
let set = new Set([1, 2, 3, 4]);
let array = Array.from(set);
Promise.all & .race
let pAll = Promise.all([ /*some promise*/ ]);
let pRace = Promise.race([ /*some promise*/ ]);
遍历器的 return
遍历器除了拥有 next
方法,还可以拥有 return
方法, return
方法主要用于循环中途退出的情况,用于释放内存,以及及时停止遍历器。
function readLinesSync(file) {
return {
[Symbol.iterator]() {
return {
next() {
return { done: false };
},
return() {
file.close()
return { done: true };
}
}
}
}
}
// 情况一:由 break 触发 return
for (let line of readLinesSync(fileName)) {
console.log(line)
break
}
// 情况二:由 error触发 return
for (let line of readLinesSync(fileName)) {
console.log(line)
throw new Error()
}