Generator
搞这么神秘 其实就是个迭代器
Generator的核心实际上就是一个Iterator
,通过yield关键字能够把函数体拆成完全可控执行片段,在函数体外部通过next来对这些执行片段进行遍历. 这和遍历array,set,map这些数据结构是一个道理.只不过generator用来遍历函数片段,而array/set/map 用来遍历元素.
对生成器执行next()操作,进行生成器的入口开始执行代码
执行到第一个yield时,向调用者返回一个值,并将函数挂起;
挂起时,函数当前的执行上下文环境和参数被保存下来;
执行到第二个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());
var g = generator() 这一句毛都不会输出,实际上这句话执行完之后,generator()形成了
内存泄漏
状态,因为函数体内部的对象被外部全局变量(g)所引用,导致generator()无法被回收. 这一句执行完后实际上g = {next:function(){xxxxxx}}
,第一个g.next()会执行
console.log(1)
,向下执行到yield关键字,将{value:2,done:false}返回給外部,然后挂起当前函数第二个g.next()会执行
(yield 2)+3
的操作,会执行把(yield 2)+3 赋值給x
的操作, 会执行console.log(3)
的操作,向下执行到yield关键字,把{value:4,done:false}返回给外部,挂起当前函数.第三个g.next()会执行
把(yield 4)赋值給y
的操作,向下扫描执行,没有发现有yield或者return了
,这时候会throw一个stopIterator
的异常,表示在这之后已经没有yield或者return语句可以迭代了
,于是把done值置为true. 返回{value:undefined,done:true}第四个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));
var g = generator(5)这一句毛都不输出,实际上这句话执行完之后,generator()形成了
内存泄漏
状态,因为函数体内部的对象被外部全局变量(g)所引用,导致generator()无法被回收. 这一句执行完后实际上g = {next:function(){xxxxxx}}
, 且函数体内的z参数被初始化成5.第一个g.next(7)
进入函数,会执行console.log(1)
,向下执行扫描到了yield关键字,于是将{value:2,done:false}返回給外部,然后挂起当前函数第二个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}返回給外部,然后挂起当前函数第三个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},然后挂起当前函数第四个g.next(10)
从上次挂起的地方进入函数,也是同理,返回{value:undefined,done:true},然后挂起当前函数
相关参考
由于Iterator/Generator无一例外的都涉及到了javascript函数从创建到执行到销毁的过程
, 涉及到了 执行上下文EC/活动对象AO/变量对象VO/内存泄漏/回收机制
等核心概念,东西多且较复杂,有兴趣的同学可以参考汤姆大叔深入理解JavasSript系列, 里面有对javascript的核心(函数/原型链)等做了详尽的介绍.