天天看點

淺析Babel的寄生式組合繼承的實作原理

作者:IT小亮

寫在前面的話

我會将過去幾年學到的 Canvas 制圖理論、實踐進行濃縮、并編輯成冊。希望通過體系化的内容組織助你快速入門、深入了解 Canvas。當然,因篇幅有限,本課程可能無法做到面面俱到,但是有了核心知識的積累,不論是繼續閱讀 Canvas 系列書籍,還是進一步擴充 Canvas 的學習領域、如 3D 制圖等,都能做到左右逢源。

本 Canvas 系列課程已經編輯成冊并陸續更新,下面是已更新章節傳送門:

《Canvas 自動化制圖必知必會-導讀篇》

《Canvas 上下文詳解》

《一文讀懂 Canvas 中的 scale 與 translate》

《一文讀懂 Canvas 中 rotate 與 skew 操作》

《Canvas 矩陣鏡像那些你不得不知的數學原理》

話不多說,直接進入正題。

1.示例代碼

父類 Parent 定義了一個箭頭函數方法,名稱為 sayHelloArrow,同時定義了一個正常屬性方法,名稱為 sayHelloCommon,同時子類 Child 繼承了父類。接下來使用 babel 對這個類進行編譯,分析編譯後的代碼構成。

class Parent {
  constructor() {
    this.type = 'parent';
  }
  sayHelloCommon() {}
  sayHelloArrow = () => {};
}
class Child extends Parent {
  constructor(props) {
    super(props);
    this.type = 'child';
  }
}
           

2.Babel 的寄生式組合繼承

2.1 Babel 是如何實作原型方法的繼承

實作原型方法的繼承,最核心的是下面的_inherits 方法。

function _inherits(subClass, superClass) {
  subClass.prototype = Object.create(superClass && superClass.prototype, {
    constructor: {
      value: subClass,
      enumerable: false,
      writable: true,
      configurable: true,
    },
  });
}
           

通過将子類的原型 prototype 設定為父類 prototype 的一個副本進而繼承父類的所有原型屬性、方法。

2.2 Babel 如何從父類繼承執行個體屬性

核心實作代碼如下,即通過 Child.__proto__ || Object.getPrototypeOf(Child)擷取父類的構造方法,接着通過 call 方法傳遞 this、執行個體屬性到父類中完成最終的建構。就這樣,子類執行個體即可擷取父類的執行個體屬性。

if (superClass)
  Object.setPrototypeOf
    ? Object.setPrototypeOf(subClass, superClass)
    : (subClass.__proto__ = superClass);
return _possibleConstructorReturn(
  this,
  (Child.__proto__ || Object.getPrototypeOf(Child)).call(this, props)
);
           

關于 setPrototypeOf、isPrototypeOf 的用法檢視下面示例即可:

const obj1 = {};
const obj2 = { foo: 'bar' };
Object.setPrototypeOf(obj1, obj2);
// setPrototypeOf
obj2.isPrototypeOf(obj1);
// true
obj1.__proto__ === obj2;
// true
Object.getPrototypeOf(obj1) 
// {foo:"bar"}
Object.getPrototypeOf(obj1)  === obj2
// 傳回true           

3.Babel 編譯完整代碼

下面是示例代碼通過Babel編譯後的完整結果,關于編譯代碼的分析可以閱讀我的其他文章,這裡不再贅述。

'use strict';
var _createClass = (function () {
  function defineProperties(target, props) {
    for (var i = 0; i < props.length; i++) {
      var descriptor = props[i];
      descriptor.enumerable = descriptor.enumerable || false;
      descriptor.configurable = true;
      if ('value' in descriptor) descriptor.writable = true;
      Object.defineProperty(target, descriptor.key, descriptor);
    }
  }
  return function (Constructor, protoProps, staticProps) {
    if (protoProps) defineProperties(Constructor.prototype, protoProps);
    if (staticProps) defineProperties(Constructor, staticProps);
    return Constructor;
  };
})();

function _possibleConstructorReturn(self, call) {
  // 確定super被調用
  if (!self) {
    throw new ReferenceError(
      "this hasn't been initialised - super() hasn't been called"
    );
  }
  return call && (typeof call === 'object' || typeof call === 'function')
    ? call
    : self;
}
// 父類必須是函數才可繼承
function _inherits(subClass, superClass) {
  if (typeof superClass !== 'function' && superClass !== null) {
    throw new TypeError(
      'Super expression must either be null or a function, not ' +
        typeof superClass
    );
  }
  subClass.prototype = Object.create(superClass && superClass.prototype, {
    constructor: {
      value: subClass,
      enumerable: false,
      writable: true,
      configurable: true,
    },
  });
  // 1.寄生式組合繼承
  if (superClass)
    Object.setPrototypeOf
      ? Object.setPrototypeOf(subClass, superClass)
      : (subClass.__proto__ = superClass);
  // 2.靜态資源通路
}
function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError('Cannot call a class as a function');
  }
}
var Parent = (function () {
  function Parent() {
    _classCallCheck(this, Parent);
    // 箭頭函數放在類的執行個體this上
    this.sayHelloArrow = function () {};
  }
  _createClass(Parent, [
    {
      // 正常函數放在類的原型prototype上
      key: 'sayHelloCommon',
      value: function sayHelloCommon() {},
    },
  ]);
  return Parent;
})();
var Child = (function (_Parent) {
  _inherits(Child, _Parent);
  // 保證繼承的父類是有效的
  function Child(props) {
    _classCallCheck(this, Child);
    return _possibleConstructorReturn(
      this,
      (Child.__proto__ || Object.getPrototypeOf(Child)).call(this, props)
      // Child.__proto__ || Object.getPrototypeOf(Child)指向了Parent
    );
  }
  return Child;
})(Parent);
           

參考資料

https://blog.csdn.net/weixin_43276913/article/details/105821919

繼續閱讀