参考:Generator 函数的含义与用法
ES6 Generator

1.基本用法

Generator函数两个特征:

  • 函数名与function之间有个 *
  • 函数内部使用yield表达式

每次调用next方法,返回一个对象,包含value和done,value的值即yield后面表达式的值

  1. function* helloWorldGenerator() {
  2. yield 'hello';
  3. yield 'world';
  4. return 'ending';
  5. }
  6. var hw = helloWorldGenerator();
  7. hw.next()
  8. // { value: 'hello', done: false }
  9. hw.next()
  10. // { value: 'world', done: false }
  11. hw.next()
  12. // { value: 'ending', done: true }
  13. hw.next()
  14. // { value: undefined, done: true }

2.yield

Generator函数内部,找两个关键字: yieldreturn

注意:y = yield 1 + 1; 此时y是undefined,并非2

遇到yield就暂停后面的操作,并把紧跟在yield后面的表达式的值,作为返回对象的value值属性。并且每次执行next才会去找yield,依次往下找,找不到yield则找return;

3.next方法的参数

yield表达式本身没有返回值,或者说总是返回 undefined ,我们看下面这个例子

  1. function* helloWorldGenerator() {
  2. var y = yield 1 + 1
  3. return y
  4. }
  5. let hw = helloWorldGenerator()
  6. console.log(hw.next()) // { value: 2, done: false }
  7. console.log(hw.next()) // { value: undefined, done: true }

第一个next返回对象的value是 表达式 1+1 的值,但整个 yield 1+1 是没有返回值的,所以最后y为undefined就结束了

那怎么改变这种情况呢?next方法可以带入一个参数,这个参数就是上一个yield表达式的值;看下面这个例子

  1. function* f() {
  2. for(var i = 0; true; i++) {
  3. var reset = yield i;
  4. if(reset) { i = -1; }
  5. }
  6. }
  7. var g = f();
  8. g.next() // { value: 0, done: false }
  9. g.next() // { value: 1, done: false }
  10. g.next(true) // { value: 0, done: false }

对于前两个g.next方法,由于没有传参,所以reset都是undefined,函数执行下去,都会走i++,所以第一次和第二次比value都在+1。当第三个next执行时,传了参数true,此时上一个yield表达式返回true, reset为true, 函数往下执行;i = -1,然后走for循环 i++; 所以最后i = 0

4.循环遍历

for of

  1. function* foo() {
  2. yield 1;
  3. yield 2;
  4. yield 3;
  5. yield 4;
  6. yield 5;
  7. return 6;
  8. }
  9. for (let v of foo()) {
  10. console.log(v);
  11. }
  12. // 1 2 3 4 5

for of 循环可以自动遍历Generator函数运行时生成的Iterator对象,此时无需调用next方法
循环会在next方法返回的对象的done属性为true时终止,并且结果不包含改对象的value;所以上面最终打印结果没有6

Array.from和扩展符

除了for…of循环以外,扩展运算符(…)、解构赋值和Array.from方法内部调用的,都是遍历器接口。这意味着,它们都可以将 Generator 函数返回的 Iterator 对象,作为参数。

  1. function* numbers () {
  2. yield 1
  3. yield 2
  4. return 3
  5. yield 4
  6. }
  7. // 扩展运算符
  8. [...numbers()] // [1, 2]
  9. // Array.from 方法
  10. Array.from(numbers()) // [1, 2]

5.next()、throw()、return()方法

next()、throw()、return()这三个方法本质上是同一件事,可以放在一起理解。它们的作用都是让 Generator 函数恢复执行,并且使用不同的语句替换yield表达式。

next()是将yield表达式替换成一个值,前面有提到,即将参数作为上一个yield表达式的返回值;这里就不举例

throw()是将yield表达式替换成一个throw语句

  1. gen.throw(new Error('出错了')); // Uncaught Error: 出错了
  2. // 相当于将 let result = yield x + y
  3. // 替换成 let result = throw(new Error('出错了'));

return()是将yield表达式替换成一个return语句

  1. gen.return(2); // Object {value: 2, done: true}
  2. // 相当于将 let result = yield x + y
  3. // 替换成 let result = return 2;

6.generator函数的this

  1. function* g() {}
  2. g.prototype.hello = function () {
  3. return 'hi!';
  4. };
  5. let obj = g();
  6. obj instanceof g // true
  7. obj.hello() // 'hi!'

注意:generator函数实例化对象,不能使用关键字new!!!

  1. function* g() {
  2. this.a = 11;
  3. }
  4. let obj = g();
  5. obj.next();
  6. obj.a // undefined

Generator函数在this对象上添加一个属性a,但是obj对象拿不到