ES6详解七 循环的秘密 - iterator 和 yield

如果学过设计模式或者java之类的肯定知道 iterator 是什么,在 Symbol.iterator 出现后,JS中也可以自己定义一个迭代器。
只要一个对象实现了正确的 Symbol.iterator 方法,那么它就可以被 for in 所遍历,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var students = {}

students[Symbol.iterator] = function() {
let index = 1;
return {
next() {
return {done: index>100, value: index++}
}
}
}

for(var i of students) {
console.log(i);
}

仔细看上面的代码就会明白迭代器是如何工作的。当执行 for(var i of students) 的时候,其实是调用了 students[Symbol.iterator]() 方法,这个方法返回了一个iterator(迭代器)。迭代器有一个next方法,for循环会不断调用这个 iterator.next方法来获取下一个值,直到返回值中的 done 属性为true的时候结束循环。

那么知道原理之后,我们可以自己来调用iterator.next来实现循环:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var students = {}

students[Symbol.iterator] = function() {
let index = 1;
return {
next() {
return {done: index>100, value: index++}
}
}
}

var iterator = students[Symbol.iterator]();

var s=iterator.next();
while(!s.done) {
console.log(s.value);
s=iterator.next();
}

上例中使用 iterator.next 和 while 结合实现了 for循环。

除了使用iterator 之外,我们还可以使用 yield 语法来实现循环,yield相对简单一些,只要通过 yield 语句把值返回即可:

1
2
3
4
5
6
7
8
9
10
11
let students = {
[Symbol.iterator]: function*() {
for(var i=0;i<=100;i++) {
yield i;
}
}
}

for(var s of students) {
console.log(s);
}

看到这里可能大家会怀疑 yield 和 iterator 到底是什么关系。这个我还不能确定,不过从用法上来说,可以基本认为 yield 只是 iterator 的语法糖,它其实就是最终生成了一个 iterator
,下面我们通过取出 yield 生成的 iterator 就可以看出来:

1
2
3
4
5
6
7
8
9
10
11
12
13
let students = {
[Symbol.iterator]: function*() {
for(var i=0;i<=100;i++) {
yield i;
}
}
}
var iterator = students[Symbol.iterator]();
var s=iterator.next();
while(!s.done) {
console.log(s.value);
s=iterator.next();
}

如上代码所示, yield 语句其实就是生成了一个 iterator ,完全和直接写 iterator 没区别,不过好处是使代码变得简洁明了,因此建议直接使用 yield 语法而不要费力的去写 iterator。

看完 上面的iterator 和 yield语法,其实看似神秘的循环也就很容易理解了。