1.next()、throw()、return() 的共同点
next()
、throw()
、return()
这三个方法本质上是同一件事,可以放在一起理解。它们的作用都是让 Generator 函数恢复执行,并且使用不同的语句替换yield
表达式。
①next()
是将yield
表达式替换成一个值。
const g = function* (x, y) { let result = yield x + y; return result; }; const gen = g(1, 2); gen.next(); // Object {value: 3, done: false} gen.next(1); // Object {value: 1, done: true} // 相当于将 let result = yield x + y // 替换成 let result = 1;
上面代码中,第二个next(1)
方法就相当于将yield
表达式替换成一个值1
。如果next
方法没有参数,就相当于替换成undefined
。
②throw()
是将yield
表达式替换成一个throw
语句。
gen.throw(new Error('出错了')); // Uncaught Error: 出错了 // 相当于将 let result = yield x + y // 替换成 let result = throw(new Error('出错了'));
③return()
是将yield
表达式替换成一个return
语句。
gen.return(2); // Object {value: 2, done: true} // 相当于将 let result = yield x + y // 替换成 let result = return 2;
2.一个 Generator 函数里面执行另一个 Generator 函数,需要用到yield*
表达式
function* foo() { yield 'a'; yield 'b'; }
从语法角度看,如果yield
表达式后面跟的是一个遍历器对象,需要在yield
表达式后面加上星号,表明它返回的是一个遍历器对象。这被称为yield*
表达式。
function* bar() { yield 'x'; yield* foo(); yield 'y'; } // 等同于 function* bar() { yield 'x'; yield 'a'; yield 'b'; yield 'y'; } // 等同于 function* bar() { yield 'x'; for (let v of foo()) { yield v; } yield 'y'; } for (let v of bar()){ console.log(v); } // "x" // "a" // "b" // "y"
任何数据结构只要有 Iterator 接口,就可以被yield*
遍历
function* gen(){ yield* ["a", "b", "c"]; } gen().next() // { value:"a", done:false } let read = (function* () { yield 'hello'; yield* 'hello'; })(); read.next().value // "hello" read.next().value // "h"
上面代码中,yield
命令后面如果不加星号,返回的是整个数组,加了星号就表示返回的是数组的遍历器对象
上面代码中,yield
表达式返回整个字符串,yield*
语句返回单个字符。因为字符串具有 Iterator 接口,所以被yield*
遍历
如果被代理的 Generator 函数有return
语句,那么就可以向代理它的 Generator 函数返回数据
function* foo() { yield 2; yield 3; return "foo"; } function* bar() { yield 1; var v = yield* foo(); console.log("v: " + v); yield 4; } var it = bar(); it.next() // {value: 1, done: false} it.next() // {value: 2, done: false} it.next() // {value: 3, done: false} it.next(); // "v: foo" // {value: 4, done: false} it.next() // {value: undefined, done: true}
上面代码在第四次调用next
方法的时候,屏幕上会有输出,这是因为函数foo
的return
语句,向函数bar
提供了返回值。
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 = [ 'a', ['b', 'c'], ['d', 'e'] ]; for(let x of iterTree(tree)) { console.log(x); } // a // b // c // d // e
3.Generator 函数的this
生成一个空对象,使用call
方法绑定 Generator 函数内部的this,就是将
构造函数调用以后,这个F.prototype就是 Generator 函数的实例对象了obj
换成F.prototype,再将
F
改成构造函数,就可以对它执行new
命令了,
function* gen() { this.a = 1; yield this.b = 2; yield this.c = 3; } function F() { return gen.call(gen.prototype); } var f = new F(); f.next(); // Object {value: 2, done: false} f.next(); // Object {value: 3, done: false} f.next(); // Object {value: undefined, done: true} f.a // 1 f.b // 2 f.c // 3
备注:文中多数内容摘自阮一峰老师文章,仅供自我学习查阅。