切图崽的自我修养-[ES6] 生成器Generator浅析

Generator

搞这么神秘 其实就是个迭代器

Generator的核心实际上就是一个Iterator,通过yield关键字能够把函数体拆成完全可控执行片段,在函数体外部通过next来对这些执行片段进行遍历. 这和遍历array,set,map这些数据结构是一个道理.只不过generator用来遍历函数片段,而array/set/map 用来遍历元素.

  1. 对生成器执行next()操作,进行生成器的入口开始执行代码

  2. 执行到第一个yield时,向调用者返回一个值,并将函数挂起;

  3. 挂起时,函数当前的执行上下文环境和参数被保存下来;

  4. 执行到第二个yield时,参数从挂起状态被重新调用,进入上次挂起的执行上下文环境继续下面的操作,到下一个yield操作时重复上面的过程

执行过程解析

    function *generator() {
      console.log(1);
      var x = (yield 2) +3
      console.log(3);
        var y = yield 4;
    }

    var g = generator(); 
    console.log(g.next());
    console.log(g.next());
    console.log(g.next());
    console.log(g.next());
    
  1. var g = generator() 这一句毛都不会输出,实际上这句话执行完之后,generator()形成了内存泄漏状态,因为函数体内部的对象被外部全局变量(g)所引用,导致generator()无法被回收. 这一句执行完后实际上 g = {next:function(){xxxxxx}},

  2. 第一个g.next()会执行console.log(1),向下执行到yield关键字,将{value:2,done:false}返回給外部,然后挂起当前函数

  3. 第二个g.next()会执行 (yield 2)+3 的操作,会执行把 (yield 2)+3 赋值給x的操作, 会执行console.log(3)的操作,向下执行到yield关键字,把{value:4,done:false}返回给外部,挂起当前函数.

  4. 第三个g.next()会执行把(yield 4)赋值給y的操作,向下扫描执行,没有发现有yield或者return了,这时候会throw一个stopIterator的异常,表示在这之后已经没有yield或者return语句可以迭代了,于是把done值置为true. 返回{value:undefined,done:true}

  5. 第四个g.next()同理,返回{value:undefined,done:true}

    function *generator(z) {
      console.log(1);
      var x = (yield 2) +z
      console.log(x);
      console.log(3);
        var y = yield 4;
        console.log(y)
    }

    var g = generator(5); 
    console.log(g.next(7)); 
    console.log(g.next(8));
    console.log(g.next(9));
    console.log(g.next(10));
    
  1. var g = generator(5)这一句毛都不输出,实际上这句话执行完之后,generator()形成了内存泄漏状态,因为函数体内部的对象被外部全局变量(g)所引用,导致generator()无法被回收. 这一句执行完后实际上 g = {next:function(){xxxxxx}}, 且函数体内的z参数被初始化成5.

  2. 第一个g.next(7)进入函数,会执行console.log(1),向下执行扫描到了yield关键字,于是将{value:2,done:false}返回給外部,然后挂起当前函数

  3. 第二个g.next(8)从上次挂起的地方进入函数,会执行 把next的参数8赋值給(yield 2)的操作, 会执行 (yield 2)+z 的操作, 会执行 把(yield 2)+z的结果赋值給x的操作, 会执行console.log(x)的操作,会执行console.log(3)的操作,继续向下执行扫描到了yield关键字,于是将{value:4,done:false}返回給外部,然后挂起当前函数

  4. 第三个g.next(9)从上次挂起的地方进入函数,会执行把next的参数9赋值給(yield 4)操作,会执行把(yield 4)赋值給y的操作,会执行console.log(y)的操作. 然后把向下执行扫描,没有发现有yield或者return了,这时候会throw一个stopIterator的异常,表示在这之后已经没有yield或者return语句可以迭代了,于是把done值置为true. 返回{value:undefined,done:true},然后挂起当前函数

  5. 第四个g.next(10)从上次挂起的地方进入函数,也是同理,返回{value:undefined,done:true},然后挂起当前函数

相关参考

由于Iterator/Generator无一例外的都涉及到了javascript函数从创建到执行到销毁的过程, 涉及到了 执行上下文EC/活动对象AO/变量对象VO/内存泄漏/回收机制 等核心概念,东西多且较复杂,有兴趣的同学可以参考汤姆大叔深入理解JavasSript系列, 里面有对javascript的核心(函数/原型链)等做了详尽的介绍.

    原文作者:大切图崽
    原文地址: https://segmentfault.com/a/1190000006739661
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞