ES 6シリーズのBabelはGeneratorをどのようにコンパイルしましたか?
11541 ワード
前言
本文は簡単にGenerator文法のコンパイル後のコードを紹介します.
Generator
具体的な実行過程は言わないで、私達は直接にBabel公式サイトのTry it outで上述のコードを貼り付けて、コードがコンパイルされてどのような形になったかを確認します.
完全に使えるコードをコンパイルできませんか?
レゲナート
完全に利用可能なコードを見たいなら、regeneratorを使ってもいいです.これはフェースブックの下のツールで、ES 6をコンパイルするためのgenerator関数です.
まずregeneratorをインストールします.
このコンパイルは700行以上の行をコンパイルしました.コンパイルしたコードはgenerator-s 5.jsを見ることができます.
つまり、コンパイルしたコードはかなり複雑です.中から大体のロジックを抜き出して、少なくとも簡単にコンパイルしたコードを走らせます.
マルク関数
簡単なコンパイル後のコードの第一段はこうです.
図
関係チェーンを構築する目的は、関係を判断する時に原生と一致するようにすることです.
リレーションチェーンの設定に加えて、mark関数の戻り値genFunは、wrap関数の第二のパラメータとしても導入されている.
make Invoke Method関数を見に来ました.
completteでは、
その後、invoke関数では、
そこで、再度
不完全ですが、利用可能なソース
もちろんこのプロセスは、テキストを見て理解するのは難しいかもしれませんが、完全ではないですが、利用可能なコードは以下の通りです.
ES 6シリーズのディレクトリアドレス:https://github.com/mqyqingfeng/Blog
ES 6シリーズは二十編ぐらい書く予定です.ES 6部分の知識点の理解を深めるために、ブロックレベルの作用領域、ラベルテンプレート、矢印関数、Symbol、Set、Map及びPromiseのシミュレーション実現、モジュールロード方案、非同期処理などの内容を重点的に説明します.
間違いや不備があったら、ぜひ指摘してください.ありがとうございます.好きだったり、何かを啓発したりすれば、starを歓迎し、作者に対しても励みになります.
本文は簡単にGenerator文法のコンパイル後のコードを紹介します.
Generator
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で上述のコードを貼り付けて、コードがコンパイルされてどのような形になったかを確認します.
/**
*
*/
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
の方法は何をしましたか?完全に使えるコードをコンパイルできませんか?
レゲナート
完全に利用可能なコードを見たいなら、regeneratorを使ってもいいです.これはフェースブックの下のツールで、ES 6をコンパイルするためのgenerator関数です.
まずregeneratorをインストールします.
npm install -g regenerator
そして、新しいgenerator.jsファイルを作成します.コードは文章の最初のコードです.命令を実行します.regenerator --include-runtime generator.js > generator-es5.js
私たちはgenerator-s 5.jsファイルでコンパイル後の完全な利用可能なコードを見ることができます.このコンパイルは700行以上の行をコンパイルしました.コンパイルしたコードはgenerator-s 5.jsを見ることができます.
つまり、コンパイルしたコードはかなり複雑です.中から大体のロジックを抜き出して、少なくとも簡単にコンパイルしたコードを走らせます.
マルク関数
簡単なコンパイル後のコードの第一段はこうです.
var _marked = /*#__PURE__*/ regeneratorRuntime.mark(helloWorldGenerator);
完全コンパイルバージョンのmark関数のソースコードを確認します.runtime.mark = function(genFun) {
genFun.__proto__ = GeneratorFunctionPrototype;
genFun.prototype = Object.create(Gp);
return genFun;
};
この中にはGenerantorFunction Prottypeと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";
このコードは複雑に見える一連の関係チェーンを構築していますが、これはES 6仕様を参照して構築された関係チェーンです.図
+@@toStringTag:s = 'Generator'
はGpで、+@@toStringTag:s = 'GeneratorFunction'
はGeneratonctionProttypeである.関係チェーンを構築する目的は、関係を判断する時に原生と一致するようにすることです.
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にこの3つの関数を追加する方法があります.// 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;
};
wrap関数リレーションチェーンの設定に加えて、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小包のその関数です.実はhello Word Generatooの関数です.ねえ、この関数です.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ではまたcompletteeが実行され、completteeが実行されます.this.next = end
のために、stop関数が実行されます.make Invoke Method関数を見に来ました.
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()
を3回目に実行した時、実行しました.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)
が実行され、completteでは、
record.type === "return"
からです.this.rval = 'ending';
this.method = "return";
this.next = "end";
そして、グローバルオブジェクトContinue Sentinelに戻ってきました.その後、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はすでに「completted」であるため、直接{ 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());
ES 6シリーズES 6シリーズのディレクトリアドレス:https://github.com/mqyqingfeng/Blog
ES 6シリーズは二十編ぐらい書く予定です.ES 6部分の知識点の理解を深めるために、ブロックレベルの作用領域、ラベルテンプレート、矢印関数、Symbol、Set、Map及びPromiseのシミュレーション実現、モジュールロード方案、非同期処理などの内容を重点的に説明します.
間違いや不備があったら、ぜひ指摘してください.ありがとうございます.好きだったり、何かを啓発したりすれば、starを歓迎し、作者に対しても励みになります.