ES6中,添加了一个新的数据类型generator。
一个generator看上去像一个函数,但可以返回多次。
generator和函数不同的是,generator由function*
定义,并且,除了return
语句,还可以用yield
返回多次。
一个用generator实现的计数器实例如下。
function *count(){
let x=0;
while(1) yield x++;
}
const f= count();
f.next() //{value: 0, done: false}
f.next() //{value: 1, done: false}
...
generator的特点主要有以下几点。
Generator
不是函数。
count()
不会立即出发执行,而是一上来就暂停
每次f.next()
都会打破暂停状态去执行,直到遇到下一个yield
或者return
遇到yield
时,会执行yeild
后面的表达式,并返回执行之后的值,然后再次进入暂停状态,此时done: false
。
遇到return
时,会返回值,执行结束,即done: true
(我这里没有ruturn, 因此可以一直调用)
每次f.next()
的返回值永远都是{value: ... , done: ...}
的形式
yield
表达式本身没有返回值,或者说总是返回undefined
。next
方法可以带一个参数,该参数就会被当作上一个yield
表达式的返回值。
function* count() {
let x = 0;
let reset;
while (1) {
reset = yield x++;
if (reset) x = 0;
}
}
const f = count();
f.next() //{value: 0, done: false}
f.next() //{value: 1, done: false}
f.next(true) //{value: 0, done: false}
由于next
方法的参数表示上一个yield
表达式的返回值,所以在第一次使用next
方法时,传递参数是无效的。V8 引擎直接忽略第一次使用next
方法时的参数,只有从第二次使用next
方法开始,参数才是有效的。
另外, Symbol.iterator
就是用generator实现的,因此可以用 for...of
循环遍历generator。
function* foo() {
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
return 6;
}
for (let v of foo()) {
console.log(v);
}
// 1 2 3 4 5
但要注意,一旦next
方法的返回对象的done
属性为true
,for...of
循环就会中止,且不包含该返回对象,所以上面代码的return
语句返回的6
,不包括在for...of
循环之中。