文章目录
基本概念
Generator 是ES6提供的一种异步编程的解决方案。Generator 可以理解成一个状态机,封装了多个内部状态。执行Generator函数会返回一个遍历器对象。Iterator
Generator 函数相比于普通函数有两个特征:
- function 命令与函数名之间有一个星号(*)
- 函数体内使用yield语句定义不同的内部状态
function* hello() {
yield 'hello';
yield 'world'
return 'ending';
}
var hw = hello();
console.log(hw.next()); //{value:'hello', done:false}
console.log(hw.next()); //{value:'world', done:false}
console.log(hw.next()); //{value:'ending', done:false}
console.log(hw.next()); //{value:undefined, done:true}
Generator函数与上一篇的Iterator相同,调用时不会执行,而是生成一个指向内部状态的指针对象。通过调用next()方法,实现指针的后移。
yield
Generator 函数返回的对象只有在调用next()方法时才会遍历下一个内部状态,yield是一种可以暂停执行的函数,yield就是暂停标志。
遍历器对象的next()方法的运行逻辑:
- 遇到yield语句就会暂停执行后面的操作,并且将紧跟在yield后面的表达式的值作为返回对象的value属性值。
- 下一次调用next()方法时再继续向下执行,直到遇到下一条yield语句。
- 如果没有遇到新的yield语句,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值作为返回对象的value属性值,done属性值变为true,表示遍历已结束。
- 如果该函数没有return语句,则返回对象的value属性值为undefined。
yield语句和return语句相似之处就是语句后面的表达式的值都作为返回对象的value属性值。不同之处在于return语句只能执行一次,而yield语句可以执行多次。
ps: yield语句只能用在Generator函数中
function* f(){
yield 1;
yield 2;
return 3;
yield 4;
}
var f = f();
console.log(f.next()); //{value:1, done:false}
console.log(f.next()); //{value:2, done:false}
console.log(f.next()); //{value:3, done:true}
console.log(f.next()); //{value:undefined, done:true}
与Iterator接口的关系
由于Generator 函数就是遍历器生成函数,因此可以把Generator赋值给对象的Symbol.iterator属性,从而使得该对象具有Iterator接口。
var myIterator = {};
myIterator[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
}
console.log([...myIterator])
//[1,2,3]
next方法的参数
next方法可以带有一个参数,该参数会被当做上一条yield语句的返回值。
//eg1
function* f(){
for(var i = 0; true; i++){
var reset = yield i;
if(reset){
i = -1;
}
}
}
var g = f();
console.log(g.next()); //{value:0, done:false}
console.log(g.next()); //{value:1, done:false}
console.log(g.next(true)); //{value:0, done:false}
//eg2
function* foo(x){
var y = 2 * (yield (x + 1) );
var z = yield(y / 3);
return (x + y + z);
}
var a = foo(5);
console.log(a.next()); //{value:6, done:false}
console.log(a.next()); //{value:NaN, done:false}
console.log(a.next()); //{value:NaN, done:true}
var b = foo(5);
console.log(b.next()); //{value:6, done:false}
console.log(b.next(12)); //{value:8, done:false}
console.log(b.next(13)); //{value:42, done:true}
for…of 循环
for…of 循环可以自动遍历Generator 函数生成的Iterator对象,且此时不再需要调用next()方法。
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
}
上面的代码只返回了5条yield语句,并没有返回最后一条return语句。因为done属性值一旦变为true,for…of 循环就会终止,且不包括该返回对象。
for…of 循环遍历对象时,也可以通过Generator函数加上这个接口就可以实现对象的遍历了。
function* object(obj){
let propKeys = Reflect.ownKeys(obj);
for(let propkey of propKeys){
yield [propkey, obj[propkey]];
}
}
let jane = { first:'Jane', last:'Doe'};
for(let [key,value] of object(jane)){
console.log(`${key}:${value}`);
//first:Jane
//last:Doe
}