摘要: ## 前言 本文就是简单介绍下 Generator 语法编译后的代码。 ## Generator ```js function* helloWorldGenerator() { yield 'hello'; yield 'world'; return 'ending'; } ``` 咱们打印下执行的结果: ```js var hw = helgit
本文就是简单介绍下 Generator 语法编译后的代码。github
function* helloWorldGenerator() { yield 'hello'; yield 'world'; return 'ending'; }
咱们打印下执行的结果:数据库
var hw = helloWorldGenerator(); console.log(hw.next()); // {value: "hello", done: false} console.log(hw.next()); // {value: "world", done: false} console.log(hw.next()); // {value: "ending", done: true} console.log(hw.next()); // {value: undefined, done: true}
具体的执行过程就不说了,咱们直接在 Babel 官网的 Try it out 粘贴上述代码,而后查看代码被编译成了什么样子:npm
/** * 咱们就称呼这个版本为简单编译版本吧 */ var _marked = /*#__PURE__*/ regeneratorRuntime.mark(helloWorldGenerator); function helloWorldGenerator() { return regeneratorRuntime.wrap( function helloWorldGenerator$(_context) { while (1) { switch ((_context.prev = _context.next)) { case 0: _context.next = 2; return "hello"; case 2: _context.next = 4; return "world"; case 4: return _context.abrupt("return", "ending"); case 5: case "end": return _context.stop(); } } }, _marked, this ); }
猛一看,好像编译后的代码还蛮少的,可是细细一看,编译后的代码确定是不能用的呀,regeneratorRuntime
是个什么鬼?哪里有声明呀?mark
和 wrap
方法又都作了什么?服务器
难道就不能编译一个完整可用的代码吗?babel
若是你想看到完整可用的代码,你可使用 regenerator,这是 facebook 下的一个工具,用于编译 ES6 的 generator 函数。异步
咱们先安装一下 regenerator:函数
npm install -g regenerator
而后新建一个 generator.js 文件,里面的代码就是文章最一开始的代码,咱们执行命令:工具
regenerator --include-runtime generator.js > generator-es5.js
咱们就能够在 generator-es5.js 文件看到编译后的完整可用的代码。this
而这一编译就编译了 700 多行…… 编译后的代码能够查看 generator-es5.js
总之编译后的代码还蛮复杂,咱们能够从中抽离出大体的逻辑,至少让简单编译的那段代码可以跑起来。
简单编译后的代码第一段是这样的:
var _marked = /*#__PURE__*/ regeneratorRuntime.mark(helloWorldGenerator);
咱们查看完整编译版本中 mark 函数的源码:
runtime.mark = function(genFun) { genFun.__proto__ = GeneratorFunctionPrototype; genFun.prototype = Object.create(Gp); return genFun; };
这其中又涉及了 GeneratorFunctionPrototype 和 Gp 变量,咱们也查看下对应的代码:
function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} ... var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(IteratorPrototype); GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype; GeneratorFunctionPrototype.constructor = GeneratorFunction; GeneratorFunctionPrototype[toStringTagSymbol] = GeneratorFunction.displayName = "GeneratorFunction";
这段代码构建了一堆看起来很复杂的关系链,其实这是参照着 ES6 规范构建的关系链:
图中 +@@toStringTag:s = 'Generator'
的就是 Gp,+@@toStringTag:s = 'GeneratorFunction'
的就是 GeneratorFunctionPrototype。
构建关系链的目的在于判断关系的时候可以跟原生的保持一致,就好比:
function* f() {} var g = f(); console.log(g.__proto__ === f.prototype); // true console.log(g.__proto__.__proto__ === f.__proto__.prototype); // true
为了简化起见,咱们能够把 Gp 先设置为一个空对象,不过正如你在上图中看到的,next()、 throw()、return() 函数都是挂载在 Gp 对象上,实际上,在完整的编译代码中,确实有为 Gp 添加这三个函数的方法:
// 117 行 function defineIteratorMethods(prototype) { ["next", "throw", "return"].forEach(function(method) { prototype[method] = function(arg) { return this._invoke(method, arg); }; }); } // 406 行 defineIteratorMethods(Gp);
为了简单起见,咱们将整个 mark 函数简化为:
runtime.mark = function(genFun) { var generator = Object.create({ next: function(arg) { return this._invoke('next', arg) } }); genFun.prototype = generator; return genFun; };
除了设置关系链以外,mark 函数的返回值 genFun 还做为了 wrap 函数的第二个参数传入:
function helloWorldGenerator() { return regeneratorRuntime.wrap( function helloWorldGenerator$(_context) { ... }, _marked, this ); }
咱们再看下 wrap 函数:
function wrap(innerFn, outerFn, self) { var generator = Object.create(outerFn.prototype); var context = new Context([]); generator._invoke = makeInvokeMethod(innerFn, self, context); return generator; }
因此当执行 var hw = helloWorldGenerator();
的时候,其实执行的是 wrap 函数,wrap 函数返回了 generator,generator 是一个对象,原型是 outerFn.prototype
, outerFn.prototype
其实就是 genFun.prototype
, genFun.prototype
是一个空对象,原型上有 next() 方法。
因此当你执行 hw.next()
的时候,执行的实际上是 hw 原型的原型上的 next 函数,next 函数执行的又是 hw 的 _invoke 函数:
generator._invoke = makeInvokeMethod(innerFn, self, context);
innerFn 就是 wrap 包裹的那个函数,其实就是 helloWordGenerato$ 函数,呐,就是这个函数:
function helloWorldGenerator$(_context) { while (1) { switch ((_context.prev = _context.next)) { case 0: _context.next = 2; return "hello"; case 2: _context.next = 4; return "world"; case 4: return _context.abrupt("return", "ending"); case 5: case "end": return _context.stop(); } } }
而 context 你能够直接理解为这样一个全局对象:
var ContinueSentinel = {}; var context = { done: false, method: "next", next: 0, prev: 0, abrupt: function(type, arg) { var record = {}; record.type = type; record.arg = arg; return this.complete(record); }, complete: function(record, afterLoc) { if (record.type === "return") { this.rval = this.arg = record.arg; this.method = "return"; this.next = "end"; } return ContinueSentinel; }, stop: function() { this.done = true; return this.rval; } };
每次 hw.next
的时候,就会修改 next 和 prev 属性的值,当在 generator 函数中 return 的时候会执行 abrupt,abrupt 中又会执行 complete,执行完 complete,由于 this.next = end
的缘故,再执行就会执行 stop 函数。
咱们来看下 makeInvokeMethod 函数:
var ContinueSentinel = {}; function makeInvokeMethod(innerFn, self, context) { var state = 'start'; return function invoke(method, arg) { if (state === 'completed') { return { value: undefined, done: true }; } context.method = method; context.arg = arg; while (true) { state = 'executing'; var record = { type: 'normal', arg: innerFn.call(self, context) }; if (record.type === "normal") { state = context.done ? 'completed' : 'yield'; if (record.arg === ContinueSentinel) { continue; } return { value: record.arg, done: context.done }; } } }; }
基本的执行过程就不分析了,咱们重点看第三次执行 hw.next()
的时候:
第三次执行 hw.next()
的时候,其实执行了
this._invoke("next", undefined);
咱们在 invoke 函数中构建了一个 record 对象:
var record = { type: "normal", arg: innerFn.call(self, context) };
而在 innerFn.call(self, context)
中,由于 _context.next 为 4 的缘故,其实执行了:
_context.abrupt("return", 'ending');
而在 abrupt 中,咱们又构建了一个 record 对象:
var record = {}; record.type = 'return'; record.arg = 'ending';
而后执行了 this.complete(record)
,
在 complete 中,由于 record.type === "return"
this.rval = 'ending'; this.method = "return"; this.next = "end";
而后返回了全局对象 ContinueSentinel,其实就是一个全局空对象。
而后在 invoke 函数中,由于 record.arg === ContinueSentinel
的缘故,没有执行后面的 return 语句,就直接进入下一个循环。
因而又执行了一遍 innerFn.call(self, context)
,此时 _context.next
为 end, 执行了 _context.stop()
, 在 stop 函数中:
this.done = true; return this.rval; // this.rval 其实就是 `ending`
因此最终返回的值为:
{ value: 'ending', done: true };
以后,咱们再执行 hw.next() 的时候,由于 state 已是 'completed' 的缘故,直接就返回 { value: undefined, done: true}
固然这个过程,看文字理解起来可能有些难度,不完整但可用的代码以下,你能够断点调试查看具体的过程:
(function() { var ContinueSentinel = {}; var mark = function(genFun) { var generator = Object.create({ next: function(arg) { return this._invoke("next", arg); } }); genFun.prototype = generator; return genFun; }; function wrap(innerFn, outerFn, self) { var generator = Object.create(outerFn.prototype); var context = { done: false, method: "next", next: 0, prev: 0, abrupt: function(type, arg) { var record = {}; record.type = type; record.arg = arg; return this.complete(record); }, complete: function(record, afterLoc) { if (record.type === "return") { this.rval = this.arg = record.arg; this.method = "return"; this.next = "end"; } return ContinueSentinel; }, stop: function() { this.done = true; return this.rval; } }; generator._invoke = makeInvokeMethod(innerFn, context); return generator; } function makeInvokeMethod(innerFn, context) { var state = "start"; return function invoke(method, arg) { if (state === "completed") { return { value: undefined, done: true }; } context.method = method; context.arg = arg; while (true) { state = "executing"; var record = { type: "normal", arg: innerFn.call(self, context) }; if (record.type === "normal") { state = context.done ? "completed" : "yield"; if (record.arg === ContinueSentinel) { continue; } return { value: record.arg, done: context.done }; } } }; } window.regeneratorRuntime = {}; regeneratorRuntime.wrap = wrap; regeneratorRuntime.mark = mark; })(); var _marked = regeneratorRuntime.mark(helloWorldGenerator); function helloWorldGenerator() { return regeneratorRuntime.wrap( function helloWorldGenerator$(_context) { while (1) { switch ((_context.prev = _context.next)) { case 0: _context.next = 2; return "hello"; case 2: _context.next = 4; return "world"; case 4: return _context.abrupt("return", "ending"); case 5: case "end": return _context.stop(); } } }, _marked, this ); } var hw = helloWorldGenerator(); console.log(hw.next()); console.log(hw.next()); console.log(hw.next()); console.log(hw.next());
ES6 系列目录地址:https://github.com/mqyqingfeng/Blog
ES6 系列预计写二十篇左右,旨在加深 ES6 部分知识点的理解,重点讲解块级做用域、标签模板、箭头函数、Symbol、Set、Map 以及 Promise 的模拟实现、模块加载方案、异步处理等内容。
若是有错误或者不严谨的地方,请务必给予指正,十分感谢。若是喜欢或者有所启发,欢迎 star,对做者也是一种鼓励。
阿里云双十一1折拼团活动:已满6人,都是最低折扣了
【满6人】1核2G云服务器99.5元一年298.5元三年 2核4G云服务器545元一年 1227元三年
【满6人】1核1G MySQL数据库 119.5元一年
【满6人】3000条国内短信包 60元每6月