In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-04 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >
Share
Shulou(Shulou.com)06/01 Report--
This article mainly explains how ES6 Babel compiles Generator. Interested friends may wish to have a look at it. The method introduced in this paper is simple, fast and practical. Let's let the editor take you to learn "how ES6 Babel compiles Generator".
Generatorfunction* helloWorldGenerator () {yield 'hello'; yield' world'; return 'ending';}
Let's print the results of the execution:
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
Let's not talk about the specific implementation process, let's paste the above code directly in the Try it out of the Babel official website, and then see what the code looks like:
/ * Let's call this version a simple compiled version * / 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);}
At first glance, it seems that the compiled code is quite little, but if you take a closer look, the compiled code must not work. What the heck is regeneratorRuntime? Where is there a statement? What do the mark and wrap methods do?
Can't you compile a fully usable code?
Regenerator
If you want to see the full code available, you can use regenerator, a tool under facebook that compiles the generator function of ES6.
Let's install regenerator first:
Npm install-g regenerator
Then create a new generator.js file, in which the code is the code at the beginning of the article, and we execute the command:
Regenerator-- include-runtime generator.js > generator-es5.js
We can see the fully available compiled code in the generator-es5.js file.
And this compilation has compiled more than 700 lines. The compiled code can view the generator-es5.js
In short, the compiled code is quite complex, and we can extract the general logic from it, at least to make the simple compiled code run.
Mark function
The first paragraph of the simply compiled code looks like this:
Var _ marked = / * # _ PURE__*/ regeneratorRuntime.mark (helloWorldGenerator)
Let's look at the source code of the mark function in the full compiled version:
Runtime.mark = function (genFun) {genFun.__proto__ = GeneratorFunctionPrototype; genFun.prototype = Object.create (Gp); return genFun;}
GeneratorFunctionPrototype and Gp variables are also involved. Let's take a look at the corresponding code:
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"
This code builds a bunch of seemingly complex relationship chains, which are actually built with reference to the ES6 specification:
In the figure, + @ @ toStringTag:s = 'Generator' is Gp,+@@toStringTag:s =' GeneratorFunction', which is GeneratorFunctionPrototype.
The purpose of building a relationship chain is to be consistent with the original when judging a relationship, such as:
Function* f () {} var g = f (); console.log (g. Protoplast = = f.prototype); / / trueconsole.log (g. Protozoa = f.__proto__.prototype); / / true
For simplicity, we can set Gp to an empty object first, but as you can see in the figure above, the next (), throw (), and return () functions are all mounted on the Gp object. In fact, in the complete compiled code, there is a way to add these three functions to Gp:
/ / 117line function defineIteratorMethods (prototype) {["next", "throw", "return"] .forEach (function (method) {prototype [method] = function (arg) {return this._invoke (method, arg);};});} / / 406line defineIteratorMethods (Gp)
For simplicity, we simplify the entire mark function to:
Runtime.mark = function (genFun) {var generator = Object.create ({next: function (arg) {return this._invoke ('next', arg)}}); genFun.prototype = generator; return genFun;}; wrap function
In addition to setting the relational chain, the return value genFun of the mark function is passed in as the second parameter of the wrap function:
Function helloWorldGenerator () {return regeneratorRuntime.wrap (function helloWorldGenerator$ (_ context) {...}, _ marked, this);}
Let's take another look at the wrap function:
Function wrap (innerFn, outerFn, self) {var generator = Object.create (outerFn.prototype); var context = new Context ([]); generator._invoke = makeInvokeMethod (innerFn, self, context); return generator;}
So when you execute var hw = helloWorldGenerator ();, you actually execute the wrap function, and the wrap function returns that generator,generator is an object, the prototype is outerFn.prototype, outerFn.prototype is actually genFun.prototype, genFun.prototype is an empty object, and there is a next () method on the prototype.
So when you execute hw.next (), you actually execute the next function on the prototype of the hw prototype, and the next function executes the hw _ invoke function:
Generator._invoke = makeInvokeMethod (innerFn, self, context)
InnerFn is the function wrapped by wrap, which is actually the helloWordGenerato$ function. This is the function:
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 ();}}
And context you can directly understand as such a global object:
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;}}
Each time hw.next, the values of the next and prev properties are modified. When return is executed in the generator function, complete is executed in abrupt,abrupt, and complete is executed after complete. Because this.next = end, the stop function is executed again.
Let's look at the makeInvokeMethod function:
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};}};}
Instead of analyzing the basic execution process, let's focus on the third execution of hw.next ():
When hw.next () is executed for the third time, it is actually executed
This._invoke ("next", undefined)
We built a record object in the invoke function:
Var record = {type: "normal", arg: innerFn.call (self, context)}
In innerFn.call (self, context), because _ context.next is 4, it actually executes:
_ context.abrupt ("return", 'ending')
In abrupt, we build another record object:
Var record = {}; record.type = 'return';record.arg =' ending'
Then this.complete (record) is executed
In complete, because record.type = = "return"
This.rval = 'ending';this.method = "return"; this.next = "end"
Then the global object ContinueSentinel is returned, which is actually a global empty object.
Then in the invoke function, because record.arg = ContinueSentinel, the following return statement is not executed and goes directly to the next loop.
So innerFn.call (self, context) is executed again, where _ context.next is end, and _ context.stop () is executed, in the stop function:
This.done = true;return this.rval; / / this.rval is actually `ending`
So the final returned value is:
{value: 'ending', done: true}
After that, when we execute hw.next (), since state is already 'completed', we directly return {value: undefined, done: true}.
Incomplete but available source code
Of course, this process may be difficult to understand by looking at the text. The incomplete but available code is as follows. You can check the specific process by debugging at breakpoints:
(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 ()); at this point, I believe you have a deeper understanding of "how ES6 Babel compiles Generator". You might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.