• Generator 函数学习笔记


    // 使用 function* 定义一个 generator 函数
    function* helloWorldGenerator() {
      yield 'hello';  // yield 关键字作为暂停的点
      yield 'world';
      return 'ending';
    }
    
    var hw = helloWorldGenerator();  // 执行 generator 函数,返回一个遍历器对象。而这时,这个函数内的代码并不会执行。
    // 调用遍历器对象的 next 方法,执行函数内的代码,执行到下一个 yield 的位置,并暂停执行
    hw.next()
    // { value: 'hello', done: false }  value 是 yield 后面跟的表达式的值,done 是 genertator 函数结束状态
    
    // 再次调用 next,执行到下一个 yield 位置
    hw.next()
    // { value: 'world', done: false }
    
    // 执行结束,value 值为 return 的值,没有 return 则为 undefined(函数没 return 返回 undefined),done 变为 true
    hw.next()
    // { value: 'ending', done: true }
    
    // 还可以无限次调用 next,但是都返回相同的对象
    hw.next()
    // { value: undefined, done: true }

    yield 不能用在普通函数中:

    var flat = function* (a) {
      // forEach 方法是个普通函数,在里面使用了 yield 会报错。解决方法是改为 for 循环
      a.forEach(function (item) {
        if (typeof item !== 'number') {
          yield* flat(item);
        } else {
          yield item;
        }
      }
    };

    yield语句如果用在一个表达式之中,必须放在圆括号里面。

    console.log('Hello' + yield); // SyntaxError
    console.log('Hello' + yield 123); // SyntaxError
    
    console.log('Hello' + (yield)); // OK
    console.log('Hello' + (yield 123)); // OK

    next方法的参数

    function* foo(x) {
      var y = 2 * (yield (x + 1));  // yield 语句在表达式中,需要将 yield 语句括起来,否则报错
      var z = yield (y / 3);
      return (x + y + z);
    }
    
    var a = foo(5);
    a.next() // Object{value:6, done:false}
    a.next() // Object{value:NaN, done:false}
    a.next() // Object{value:NaN, done:true}
    
    var b = foo(5);
    b.next() // { value:6, done:false }  调用第一次 next 开始执行,得到第一个 yield 的返回值 6。由于 next 参数为上一个 yield 语句的值,所以第一个 next 传入参数没有意义
    b.next(12) // { value:8, done:false }  调用 next 方法时注入了数据,作为上一个 yield 语句的值,得到 var y = 2 * 12
    b.next(13) // { value:42, done:true }  得到 var z = 13

    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  这里需要注意,一旦next方法的返回对象的done属性为truefor...of循环就会中止,且不包含该返回对象,所以上面代码的return语句返回的6,不包括在for...of循环之中。

     原生的JavaScript对象没有遍历接口,无法使用for...of循环,通过Generator函数为它加上这个接口,就可以用了。

    // 第一种方法
    function* objectEntries(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 objectEntries(jane)) {
      console.log(`${key}: ${value}`);
    }
    
    // 第二种方法
    function* objectEntries() {
      let propKeys = Object.keys(this);
    
      for (let propKey of propKeys) {
        yield [propKey, this[propKey]];
      }
    }
    
    let jane = { first: 'Jane', last: 'Doe' };
    
    jane[Symbol.iterator] = objectEntries;
    
    for (let [key, value] of jane) {
      console.log(`${key}: ${value}`);
    }

    Generator.prototype.throw()

    Generator函数返回的遍历器对象,都有一个throw方法,可以在函数体外抛出错误,然后在Generator函数体内捕获。

    var g = function* () {
    // 使用 try...catch... 进行异常捕获
    try { yield; } catch (e) { console.log('内部捕获', e); } }; var i = g(); i.next(); try { i.throw('a'); // 这里使用 throw 方法抛出的错误,会由 generator 函数内的 catch 处理 i.throw('b'); // generator 内的 catch 已经执行过了,就不会再被 generator 的 catch 捕获了,由外部的 catch 捕获 } catch (e) { console.log('外部捕获', e); } // 内部捕获 a // 外部捕获 b

    如果Generator函数内部没有部署try...catch代码块,那么throw方法抛出的错误,将被外部try...catch代码块捕获。

    如果Generator函数内部和外部,都没有部署try...catch代码块,那么程序将报错,直接中断执行。

    throw方法被捕获以后,会附带执行下一条yield语句。也就是说,会附带执行一次next方法。

    var gen = function* gen(){
      try {
        yield console.log('a');
      } catch (e) {
        // ...
      }
      yield console.log('b');  // throw 方法会附带执行 next,从而执行到这个 yield 位置
      yield console.log('c');
    }
    
    var g = gen();
    g.next() // a
    g.throw() // b
    g.next() // c

    Generator.prototype.return()

    Generator函数返回的遍历器对象,还有一个return方法,可以返回给定的值,并且终结遍历Generator函数。

    function* gen() {
      yield 1;
      yield 2;
      yield 3;
    }
    
    var g = gen();
    
    g.next()        // { value: 1, done: false }
    g.return('foo') // { value: "foo", done: true }  value 值变成了 return 的参数
    g.next()        // { value: undefined, done: true }  return 方法 导致 generator 函数结束,所以 value 为 undefined

    yield*语句

    function* foo() {
      yield 'a';
      yield 'b';
    }
    
    function* bar() {
      yield 'x';
      // foo();  如果只是单纯的执行 foo() 函数,只是得到一个遍历器对象,并不会产生什么效果。
      yield* foo();  // 使用了 yield* 语句,在遍历的时候才会遍历这个 generator 函数内部的 generator 函数。
      yield 'y';
    }
    
    for (let v of bar()){
      console.log(v);
    }
    // "x"
    // "a"
    // "b"
    // "y"
    function* gen(){
      yield* ["a", "b", "c"];  // 数组、字符串等,带有 iterator 接口的,都可以被 yield* 遍历
    }
    
    gen().next() // { value:"a", done:false }

    Generator与状态机

    var clock = function*() {
      while (true) {
        console.log('Tick!');  // 执行状态1代码
        yield;
        console.log('Tock!');  // 执行状态2代码
        yield;
      }
    };

    每次调用 next() 就可以在两种状态间切换执行,而不需要使用一个布尔变量来做判断

  • 相关阅读:
    Anaconda3的Jupyter notebook调用ArcGISPro的Arcpy
    ArcMap 创建空间邻接矩阵
    Anaconda3的Jupyter notebook切换Python3和Python2环境并调用Arcpy
    PHP.MVC的模板标签系统(二)
    PHP汉字转拼音的类
    Linux 解压命令大全
    ASP应用之模板采用
    PHP中的串行化变量和序列化对象(一)
    CSS常用技巧介绍
    ASP实现多图片上传(一)
  • 原文地址:https://www.cnblogs.com/3body/p/6020428.html
Copyright © 2020-2023  润新知