ES6:Generator的来龙去脉
概要
-
Generator函数是ES6提供的一种异步编程解决方法.可以将它理解为一个状态机,封装了多个内部状态。-
-
执行Generator函数会返回一个遍历器对象,可依次遍历函数内部的每一个状态;
-
Generator函数有两个特征:
- function 与函数名之间有一个星号;
- 函数体内部使用yield语句定义不同的内部状态。
-
示例:
function*helloWorldGenerator(){ yield '听不见!'; yield '重来!'; return '很有精神!' } let hw = helloWorldGenerator(); hw.next(); // { value : '听不见!',done : false } hw.next(); // { value : '重来!',done : false } hw.next(); // { value : '很有精神!',done : true } hw.next(); // { value : undefined , done : true }
-
利用这个特性,可以实现函数的异步编程机制;
yield表达式
-
遍历器的next方法运行机制如下:
- 遇到yield语句就暂停执行后面的操作,返回yield表达式的值作为返回的对象的value属性值;
- 下一次调用next时继续执行,知道遇到 下一个yield语句;
- 若无yield,直接运行到return语句;
- 若无return语句,则返回一个value值为undefined的对象。
-
yield必须放在Generator函数中才能够使用;
-
yield若用在另一个表达式中必须添加圆括号!
console.log('hello' + yield 123); ///error console.log('hello' + (yield 123)); //ok
next方法的参数
-
next方法可以带有一个参数,该参数会被当作上一条语句的返回值。当next不带参数时,则返回undefined;
function* f(){ for(let i=0;ture;i++){ let reset = yield i; if(reset){ i = -1; } } } let g = f(); g.next() //{value : 0,done : false}; g.next() //{value : 1,done : false}; g.next(true) //{value : 0,done : false};
for…of方法
-
for…of循环可以自动遍历Generator函数生成的Iterator对象,从而无需再使用next方法;
function* foo(){ yield 1; yield 2; yield 3; yield 4; yield 5; } for (let v of foo()){ console.log(v); }
yield*表达式
-
若在一个Generator调用另一个Generator函数,默认是没有效果的;
function* foo(){ yield 'a'; yield 'b'; } function bar(){ yield 1; foo(); yield 2; } for(let i of bar()){ console.log(i); } >>1 >>2
-
可以看出,直接调用是无法在for…of中执行foo()的;
-
此时就需要用到yield*语句,因此上面的代码可以修改成这样:
function* foo(){ yield 'a'; yield 'b'; } function bar(){ yield 1; yield* foo(); yield 2; } for(let i of bar()){ console.log(i); } >>1 >>'a' >>'b' >>2
-
如果yield*后面跟着一个数组,就会遍历该数组的成员:
function* foo(){ yield [1,2,3,4,5]; } foo().next(); //[1,2,3,4,5] function* foo(){ yield* [1,2,3,4,5]; } foo().next(); //{value : 1,done : false}
-
yield* 表达式可以很快地遍历出嵌套数组的所有成员;
function* iterTree(tree){ if(Array.isArray(tree)){ for(let i=0;i<tree.length;i++); yield* iterTree(tree[i]); }else{ yield tree; } } const tree = [1,[2,3],[4,5]]; for(let x of iterTree(tree)){ console.log(x); } >> 1 >> 2 >> 3 >> 4 >> 5
Generator函数的this
-
Generator函数返回的遍历器为原Generator函数的实例,也继承了原函数的原型;
-
因此,Generator函数的遍历器无法直接访问原函数的属性;
function* foo(){ this.a = 1; } let obj = foo(); console.log(obj.a); //undefined
-
要获得原函数的属性,可通过变通的方法进行获取——使用call方法实现绑定Generator函数内部的this;
function* foo(){ this.a = 1; yield this.b = 2; yield this.c = 3; } let obj = { }; let bar = foo.call(obj); bar.next(); //{value : 2 , done : false}; bar.next(); //{value : 3 , done : false}; console.log(obj.a) // 1 console.log(obj.b) // 2 console.log(obj.c) // 3