iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >ES6 Babel怎么编译Generator
  • 735
分享到

ES6 Babel怎么编译Generator

2023-06-05 09:06:18 735人浏览 独家记忆
摘要

本篇内容主要讲解“es6 Babel怎么编译Generator”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“ES6 Babel怎么编译Generator”吧!Generatorfunction*

本篇内容主要讲解“es6 Babel怎么编译Generator”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“ES6 Babel怎么编译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

具体的执行过程就不说了,我们直接在 Babel 官网的 Try it out 粘贴上述代码,然后查看代码被编译成了什么样子:

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  );}

猛一看,好像编译后的代码还蛮少的,但是细细一看,编译后的代码肯定是不能用的呀,regeneratorRuntime是个什么鬼?哪里有声明呀?mark 和 wrap 方法又都做了什么?

难道就不能编译一个完整可用的代码吗?

regenerator

如果你想看到完整可用的代码,你可以使用 regenerator,这是 facebook 下的一个工具,用于编译 ES6 的 generator 函数。

我们先安装一下 regenerator:

npm install -g regenerator

然后新建一个 generator.js 文件,里面的代码就是文章最一开始的代码,我们执行命令:

regenerator --include-runtime generator.js > generator-es5.js

我们就可以在 generator-es5.js 文件看到编译后的完整可用的代码。

而这一编译就编译了 700 多行…… 编译后的代码可以查看 generator-es5.js

总之编译后的代码还蛮复杂,我们可以从中抽离出大致的逻辑,至少让简单编译的那段代码能够跑起来。

mark 函数

简单编译后的代码第一段是这样的:

var _marked =  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 规范构建的关系链:

ES6 Babel怎么编译Generator

图中 +@@toStringTag:s = 'Generator' 的就是 Gp,+@@toStringTag:s = 'GeneratorFunction' 的就是 GeneratorFunctionPrototype。

构建关系链的目的在于判断关系的时候能够跟原生的保持一致,就比如:

function* f() {}var g = f();console.log(g.__proto__ === f.prototype); // trueconsole.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;};

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.prototypeouterFn.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 Babel怎么编译Generator”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

--结束END--

本文标题: ES6 Babel怎么编译Generator

本文链接: https://www.lsjlt.com/news/242234.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • ES6 Babel怎么编译Generator
    本篇内容主要讲解“ES6 Babel怎么编译Generator”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“ES6 Babel怎么编译Generator”吧!Generatorfunction*...
    99+
    2023-06-05
  • phpstorm下怎么设置babel编译
    这篇文章将为大家详细讲解有关phpstorm下怎么设置babel编译,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。phpstorm下设置babel 编译在phpstorm下使用扩展工具直接进行babel ...
    99+
    2023-06-14
  • 为什么要用Babel编译Typescript
    这篇文章主要介绍“为什么要用Babel编译Typescript”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“为什么要用Babel编译Typescript”文章能帮助大家解决问题。typescript...
    99+
    2023-06-05
  • es6中的generator怎么用
    本篇内容介绍了“es6中的generator怎么用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成! ...
    99+
    2024-04-02
  • gulp+browserify编译es6错误怎么解决
    这篇“gulp+browserify编译es6错误怎么解决”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“gulp+brows...
    99+
    2023-06-17
  • WebStorm ES6语法支持设置&babel使用及自动编译的示例分析
    这篇文章将为大家详细讲解有关WebStorm ES6语法支持设置&babel使用及自动编译的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。一、语法支持设置...
    99+
    2024-04-02
  • ES6中Generator自动执行怎么实现
    这篇文章主要讲解了“ES6中Generator自动执行怎么实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“ES6中Generator自动执行怎么实现”吧!单个异步任务var fe...
    99+
    2023-06-17
  • ES6中迭代器、Generator函数怎么用
    小编给大家分享一下ES6中迭代器、Generator函数怎么用,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!一、迭代器之前再聊迭...
    99+
    2024-04-02
  • Es6 中Generator函数有什么用
    本篇文章给大家分享的是有关Es6 中Generator函数有什么用,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。ECMAScript 6 (简...
    99+
    2024-04-02
  • Babel怎么用
    小编给大家分享一下Babel怎么用,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!Babel是一个广泛使用的转码器,可以将ES6代...
    99+
    2024-04-02
  • c++怎么编译
    非常抱歉,由于您没有提供文章标题,我无法为您生成一篇高质量的文章。请您提供文章标题,我将尽快为您生成一篇优质的文章。...
    99+
    2024-05-15
  • 怎么编译Oozie
    本篇内容介绍了“怎么编译Oozie”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、什么是工作流?工作流(WorkFlow)就是工作流程的计...
    99+
    2023-06-02
  • c#怎么编译
    要在 windows 上编译 c# 代码,您可以使用:visual studio:创建 c# 项目,输入代码,并生成解决方案。.net cli 工具包:安装工具包,导航到代码目录,并使用...
    99+
    2024-04-04
    c#
  • Python中怎么实现编译和反编译
    这篇文章将为大家详细讲解有关Python中怎么实现编译和反编译,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。一、用Pyinstaller打包python代码1. 安装Pyinstaller安...
    99+
    2023-06-16
  • 如何使用webpack4编译并压缩ES6代码
    这篇文章将为大家详细讲解有关如何使用webpack4编译并压缩ES6代码,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。前段时间第一次接触了layabox游戏引擎做了一个项...
    99+
    2024-04-02
  • Linux怎么编译qt
    这篇文章主要介绍了Linux怎么编译qt的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Linux怎么编译qt文章都会有所收获,下面我们一起来看看吧。首先下载源码包qt-everywhere-opens...
    99+
    2023-06-28
  • maven怎么编译kafka
    要使用Maven编译Kafka,你需要执行以下步骤: 下载Kafka源代码并解压缩。 确保你已经安装了Java和Maven,...
    99+
    2023-10-21
    maven kafka
  • 怎么编译链接
    本篇内容介绍了“怎么编译链接”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!不知道大家平时编程过程中使用动态...
    99+
    2024-04-02
  • webpack6中Babel怎么用
    这篇文章主要介绍了webpack6中Babel怎么用,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。在 webpack 中编写 JavaScr...
    99+
    2024-04-02
  • 怎么编译安装Nginx
    这篇文章主要讲解了“怎么编译安装Nginx”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么编译安装Nginx”吧!前言由于微信小程序要使用Https,但是又不能修改已有线上的配置。所以最简...
    99+
    2023-06-16
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作