溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

ES6 Babel怎么編譯Generator

發(fā)布時(shí)間:2021-12-23 10:13:37 來源:億速云 閱讀:144 作者:iii 欄目:大數(shù)據(jù)

本篇內(nèi)容主要講解“ES6 Babel怎么編譯Generator”,感興趣的朋友不妨來看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“ES6 Babel怎么編譯Generator”吧!

Generator

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}

Babel

具體的執(zhí)行過程就不說了,我們直接在 Babel 官網(wǎng)的  Try it out  粘貼上述代碼,然后查看代碼被編譯成了什么樣子:

/**
 * 我們就稱呼這個(gè)版本為簡(jiǎn)單編譯版本吧
 */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是個(gè)什么鬼?哪里有聲明呀?mark 和 wrap 方法又都做了什么?

難道就不能編譯一個(gè)完整可用的代碼嗎?

regenerator

如果你想看到完整可用的代碼,你可以使用  regenerator ,這是 facebook 下的一個(gè)工具,用于編譯 ES6 的 generator 函數(shù)。

我們先安裝一下 regenerator:

npm install -g regenerator

然后新建一個(gè) generator.js 文件,里面的代碼就是文章最一開始的代碼,我們執(zhí)行命令:

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

我們就可以在 generator-es5.js 文件看到編譯后的完整可用的代碼。

而這一編譯就編譯了 700 多行…… 編譯后的代碼可以查看  generator-es5.js

總之編譯后的代碼還蠻復(fù)雜,我們可以從中抽離出大致的邏輯,至少讓簡(jiǎn)單編譯的那段代碼能夠跑起來。

mark 函數(shù)

簡(jiǎn)單編譯后的代碼第一段是這樣的:

var _marked = /*#__PURE__*/ regeneratorRuntime.mark(helloWorldGenerator);

我們查看完整編譯版本中 mark 函數(shù)的源碼:

runtime.mark = function(genFun) {
  genFun.__proto__ = GeneratorFunctionPrototype;
  genFun.prototype = Object.create(Gp);  return genFun;
};

這其中又涉及了 GeneratorFunctionPrototype 和 Gp 變量,我們也查看下對(duì)應(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)系鏈,其實(shí)這是參照著  ES6 規(guī)范 構(gòu)建的關(guān)系鏈:

ES6 Babel怎么編譯Generator

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

構(gòu)建關(guān)系鏈的目的在于判斷關(guān)系的時(shí)候能夠跟原生的保持一致,就比如:

function* f() {}var g = f();console.log(g.__proto__ === f.prototype); // trueconsole.log(g.__proto__.__proto__ === f.__proto__.prototype); // true

為了簡(jiǎn)化起見,我們可以把 Gp 先設(shè)置為一個(gè)空對(duì)象,不過正如你在上圖中看到的,next()、 throw()、return() 函數(shù)都是掛載在 Gp 對(duì)象上,實(shí)際上,在完整的編譯代碼中,確實(shí)有為 Gp 添加這三個(gè)函數(shù)的方法:

// 117 行function defineIteratorMethods(prototype) {
  ["next", "throw", "return"].forEach(function(method) {
    prototype[method] = function(arg) {      return this._invoke(method, arg);
    };
  });
}// 406 行defineIteratorMethods(Gp);

為了簡(jiǎn)單起見,我們將整個(gè) mark 函數(shù)簡(jiǎn)化為:

runtime.mark = function(genFun) {  var generator = Object.create({    next: function(arg) {      return this._invoke('next', arg)
    }
  });
  genFun.prototype = generator;  return genFun;
};

wrap 函數(shù)

除了設(shè)置關(guān)系鏈之外,mark 函數(shù)的返回值 genFun 還作為了 wrap 函數(shù)的第二個(gè)參數(shù)傳入:

function helloWorldGenerator() {  return regeneratorRuntime.wrap(    function helloWorldGenerator$(_context) {
      ...
    },
    _marked,    this
  );
}

我們?cè)倏聪?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(); 的時(shí)候,其實(shí)執(zhí)行的是 wrap 函數(shù),wrap 函數(shù)返回了 generator,generator 是一個(gè)對(duì)象,原型是 outerFn.prototypeouterFn.prototype 其實(shí)就是 genFun.prototype, genFun.prototype 是一個(gè)空對(duì)象,原型上有 next() 方法。

所以當(dāng)你執(zhí)行 hw.next() 的時(shí)候,執(zhí)行的其實(shí)是 hw 原型的原型上的 next 函數(shù),next 函數(shù)執(zhí)行的又是 hw 的 _invoke 函數(shù):

generator._invoke = makeInvokeMethod(innerFn, self, context);

innerFn 就是 wrap 包裹的那個(gè)函數(shù),其實(shí)就是 helloWordGenerato$ 函數(shù),吶,就是這個(gè)函數(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 你可以直接理解為這樣一個(gè)全局對(duì)象:

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 的時(shí)候,就會(huì)修改 next 和 prev 屬性的值,當(dāng)在 generator 函數(shù)中 return 的時(shí)候會(huì)執(zhí)行 abrupt,abrupt 中又會(huì)執(zhí)行 complete,執(zhí)行完 complete,因?yàn)?nbsp;this.next = end 的緣故,再執(zhí)行就會(huì)執(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í)行過程就不分析了,我們重點(diǎn)看第三次執(zhí)行 hw.next() 的時(shí)候:

第三次執(zhí)行 hw.next() 的時(shí)候,其實(shí)執(zhí)行了

this._invoke("next", undefined);

我們?cè)?invoke 函數(shù)中構(gòu)建了一個(gè) record 對(duì)象:

var record = {
  type: "normal",
  arg: innerFn.call(self, context)
};

而在 innerFn.call(self, context) 中,因?yàn)?_context.next 為 4 的緣故,其實(shí)執(zhí)行了:

_context.abrupt("return", 'ending');

而在 abrupt 中,我們又構(gòu)建了一個(gè) record 對(duì)象:

var record = {};
record.type = 'return';
record.arg = 'ending';

然后執(zhí)行了 this.complete(record),

在 complete 中,因?yàn)?nbsp;record.type === "return"

this.rval = 'ending';this.method = "return";this.next = "end";

然后返回了全局對(duì)象 ContinueSentinel,其實(shí)就是一個(gè)全局空對(duì)象。

然后在 invoke 函數(shù)中,因?yàn)?nbsp;record.arg === ContinueSentinel 的緣故,沒有執(zhí)行后面的 return 語句,就直接進(jìn)入下一個(gè)循環(huán)。

于是又執(zhí)行了一遍 innerFn.call(self, context),此時(shí) _context.next 為 end, 執(zhí)行了 _context.stop(), 在 stop 函數(shù)中:

this.done = true;return this.rval; // this.rval 其實(shí)就是 `ending`

所以最終返回的值為:

{  value: 'ending',
  done: true
};

之后,我們?cè)賵?zhí)行 hw.next() 的時(shí)候,因?yàn)?state 已經(jīng)是 'completed' 的緣故,直接就返回 { value: undefined, done: true}

不完整但可用的源碼

當(dāng)然這個(gè)過程,看文字理解起來可能有些難度,不完整但可用的代碼如下,你可以斷點(diǎn)調(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());

到此,相信大家對(duì)“ES6 Babel怎么編譯Generator”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI