本篇內(nèi)容主要講解“ES6 Babel怎么編譯Generator”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習(xí)“ES6 Babel怎么編譯Generator”吧!
專注于為中小企業(yè)提供成都網(wǎng)站建設(shè)、網(wǎng)站制作服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)仁化免費做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動了成百上千企業(yè)的穩(wěn)健成長,幫助中小企業(yè)通過網(wǎng)站建設(shè)實現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。
function* helloWorldGenerator() { yield 'hello'; yield 'world'; return 'ending'; }
我們打印下執(zhí)行的結(jié)果:
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}
具體的執(zhí)行過程就不說了,我們直接在 Babel 官網(wǎng)的 Try it out 粘貼上述代碼,然后查看代碼被編譯成了什么樣子:
/** * 我們就稱呼這個版本為簡單編譯版本吧 */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 ); }
猛一看,好像編譯后的代碼還蠻少的,但是細(xì)細(xì)一看,編譯后的代碼肯定是不能用的呀,regeneratorRuntime
是個什么鬼?哪里有聲明呀?mark
和 wrap
方法又都做了什么?
難道就不能編譯一個完整可用的代碼嗎?
如果你想看到完整可用的代碼,你可以使用 regenerator ,這是 facebook 下的一個工具,用于編譯 ES6 的 generator 函數(shù)。
我們先安裝一下 regenerator:
npm install -g regenerator
然后新建一個 generator.js 文件,里面的代碼就是文章最一開始的代碼,我們執(zhí)行命令:
regenerator --include-runtime generator.js > generator-es5.js
我們就可以在 generator-es5.js 文件看到編譯后的完整可用的代碼。
而這一編譯就編譯了 700 多行…… 編譯后的代碼可以查看 generator-es5.js
總之編譯后的代碼還蠻復(fù)雜,我們可以從中抽離出大致的邏輯,至少讓簡單編譯的那段代碼能夠跑起來。
簡單編譯后的代碼第一段是這樣的:
var _marked = /*#__PURE__*/ regeneratorRuntime.mark(helloWorldGenerator);
我們查看完整編譯版本中 mark 函數(shù)的源碼:
runtime.mark = function(genFun) { genFun.__proto__ = GeneratorFunctionPrototype; genFun.prototype = Object.create(Gp); return genFun; };
這其中又涉及了 GeneratorFunctionPrototype 和 Gp 變量,我們也查看下對應(yīng)的代碼:
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";
這段代碼構(gòu)建了一堆看起來很復(fù)雜的關(guān)系鏈,其實這是參照著 ES6 規(guī)范 構(gòu)建的關(guān)系鏈:
圖中 +@@toStringTag:s = 'Generator'
的就是 Gp,+@@toStringTag:s = 'GeneratorFunction'
的就是 GeneratorFunctionPrototype。
構(gòu)建關(guān)系鏈的目的在于判斷關(guān)系的時候能夠跟原生的保持一致,就比如:
function* f() {}var g = f();console.log(g.__proto__ === f.prototype); // trueconsole.log(g.__proto__.__proto__ === f.__proto__.prototype); // true
為了簡化起見,我們可以把 Gp 先設(shè)置為一個空對象,不過正如你在上圖中看到的,next()、 throw()、return() 函數(shù)都是掛載在 Gp 對象上,實際上,在完整的編譯代碼中,確實有為 Gp 添加這三個函數(shù)的方法:
// 117 行function defineIteratorMethods(prototype) { ["next", "throw", "return"].forEach(function(method) { prototype[method] = function(arg) { return this._invoke(method, arg); }; }); }// 406 行defineIteratorMethods(Gp);
為了簡單起見,我們將整個 mark 函數(shù)簡化為:
runtime.mark = function(genFun) { var generator = Object.create({ next: function(arg) { return this._invoke('next', arg) } }); genFun.prototype = generator; return genFun; };
除了設(shè)置關(guān)系鏈之外,mark 函數(shù)的返回值 genFun 還作為了 wrap 函數(shù)的第二個參數(shù)傳入:
function helloWorldGenerator() { return regeneratorRuntime.wrap( function helloWorldGenerator$(_context) { ... }, _marked, this ); }
我們再看下 wrap 函數(shù):
function wrap(innerFn, outerFn, self) { var generator = Object.create(outerFn.prototype); var context = new Context([]); generator._invoke = makeInvokeMethod(innerFn, self, context); return generator; }
所以當(dāng)執(zhí)行 var hw = helloWorldGenerator();
的時候,其實執(zhí)行的是 wrap 函數(shù),wrap 函數(shù)返回了 generator,generator 是一個對象,原型是 outerFn.prototype
, outerFn.prototype
其實就是 genFun.prototype
, genFun.prototype
是一個空對象,原型上有 next() 方法。
所以當(dāng)你執(zhí)行 hw.next()
的時候,執(zhí)行的其實是 hw 原型的原型上的 next 函數(shù),next 函數(shù)執(zhí)行的又是 hw 的 _invoke 函數(shù):
generator._invoke = makeInvokeMethod(innerFn, self, context);
innerFn 就是 wrap 包裹的那個函數(shù),其實就是 helloWordGenerato$ 函數(shù),吶,就是這個函數(shù):
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 屬性的值,當(dāng)在 generator 函數(shù)中 return 的時候會執(zhí)行 abrupt,abrupt 中又會執(zhí)行 complete,執(zhí)行完 complete,因為 this.next = end
的緣故,再執(zhí)行就會執(zhí)行 stop 函數(shù)。
我們來看下 makeInvokeMethod 函數(shù):
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 }; } } }; }
基本的執(zhí)行過程就不分析了,我們重點看第三次執(zhí)行 hw.next()
的時候:
第三次執(zhí)行 hw.next()
的時候,其實執(zhí)行了
this._invoke("next", undefined);
我們在 invoke 函數(shù)中構(gòu)建了一個 record 對象:
var record = { type: "normal", arg: innerFn.call(self, context) };
而在 innerFn.call(self, context)
中,因為 _context.next 為 4 的緣故,其實執(zhí)行了:
_context.abrupt("return", 'ending');
而在 abrupt 中,我們又構(gòu)建了一個 record 對象:
var record = {}; record.type = 'return'; record.arg = 'ending';
然后執(zhí)行了 this.complete(record)
,
在 complete 中,因為 record.type === "return"
this.rval = 'ending';this.method = "return";this.next = "end";
然后返回了全局對象 ContinueSentinel,其實就是一個全局空對象。
然后在 invoke 函數(shù)中,因為 record.arg === ContinueSentinel
的緣故,沒有執(zhí)行后面的 return 語句,就直接進(jìn)入下一個循環(huán)。
于是又執(zhí)行了一遍 innerFn.call(self, context)
,此時 _context.next
為 end, 執(zhí)行了 _context.stop()
, 在 stop 函數(shù)中:
this.done = true;return this.rval; // this.rval 其實就是 `ending`
所以最終返回的值為:
{ value: 'ending', done: true };
之后,我們再執(zhí)行 hw.next() 的時候,因為 state 已經(jīng)是 'completed' 的緣故,直接就返回 { value: undefined, done: true}
當(dāng)然這個過程,看文字理解起來可能有些難度,不完整但可用的代碼如下,你可以斷點調(diào)試查看具體的過程:
(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 Babel怎么編譯Generator”有了更深的了解,不妨來實際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
網(wǎng)頁題目:ES6Babel怎么編譯Generator
URL地址:http://www.rwnh.cn/article24/pgsoce.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供做網(wǎng)站、網(wǎng)站建設(shè)、軟件開發(fā)、手機(jī)網(wǎng)站建設(shè)、網(wǎng)站內(nèi)鏈、網(wǎng)站排名
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)