ES6 Iterator
Iterator 迭代器,终于也加入了 JS 的大本营,迭代器为一个可迭代的对象,可迭代等于可遍历,那么 ES 中,迭代器该怎么写?
简单谈谈
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 接口的对象:
ArrayMapSetString- 函数的 
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()
}