babel 如何轉義extends

接着上一篇文章,我們繼續進行extends的解析。

開始之前,我們先要回顧一下ES5的繼承,以前實現繼承也不是一件容易的事情,常用的方法有構造函數繼承,原型繼承,組合繼承,寄生式繼承和寄生組合式繼承等,構造函數繼承中每個子類的屬性和方法都是獨立,太浪費內存,原型繼承雖然實現了方法的共享,但是存在子類修改公共方法,影響其他子類的問題等,那麼babel是如何來處理繼承的呢,接下來我們通過babel官網提供的工具看一下:

// 原始代碼
class Father {
    name = 'iwen'
    age = 18
    run() {}
    run2() {}
    static talk() { }
    static age2 = 19
}

class Child  extends Father{};


// babel 處理後代碼
"use strict";

function _typeof(obj) {
  "@babel/helpers - typeof";
  if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
    _typeof = function _typeof(obj) {
      return typeof obj;
    };
  } else {
    _typeof = function _typeof(obj) {
      return obj &&
        typeof Symbol === "function" &&
        obj.constructor === Symbol &&
        obj !== Symbol.prototype
        ? "symbol"
        : typeof obj;
    };
  }
  return _typeof(obj);
}

function _inherits(subClass, superClass) {
  if (typeof superClass !== "function" && superClass !== null) {
    throw new TypeError("Super expression must either be null or a function");
  }
  subClass.prototype = Object.create(superClass && superClass.prototype, {
    constructor: { value: subClass, writable: true, configurable: true },
  });
  if (superClass) _setPrototypeOf(subClass, superClass);
}

function _setPrototypeOf(o, p) {
  _setPrototypeOf =
    Object.setPrototypeOf ||
    function _setPrototypeOf(o, p) {
      o.__proto__ = p;
      return o;
    };
  return _setPrototypeOf(o, p);
}

function _createSuper(Derived) {
  var hasNativeReflectConstruct = _isNativeReflectConstruct();
  return function () {
    var Super = _getPrototypeOf(Derived),
      result;
    if (hasNativeReflectConstruct) {
      var NewTarget = _getPrototypeOf(this).constructor;
      result = Reflect.construct(Super, arguments, NewTarget);
    } else {
      result = Super.apply(this, arguments);
    }
    return _possibleConstructorReturn(this, result);
  };
}

function _possibleConstructorReturn(self, call) {
  if (call && (_typeof(call) === "object" || typeof call === "function")) {
    return call;
  }
  return _assertThisInitialized(self);
}

function _assertThisInitialized(self) {
  if (self === void 0) {
    throw new ReferenceError(
      "this hasn't been initialised - super() hasn't been called"
    );
  }
  return self;
}

function _isNativeReflectConstruct() {
  if (typeof Reflect === "undefined" || !Reflect.construct) return false;
  if (Reflect.construct.sham) return false;
  if (typeof Proxy === "function") return true;
  try {
    Date.prototype.toString.call(Reflect.construct(Date, [], function () {}));
    return true;
  } catch (e) {
    return false;
  }
}

function _getPrototypeOf(o) {
  _getPrototypeOf = Object.setPrototypeOf
    ? Object.getPrototypeOf
    : function _getPrototypeOf(o) {
        return o.__proto__ || Object.getPrototypeOf(o);
      };
  return _getPrototypeOf(o);
}

function _instanceof(left, right) {
  if (
    right != null &&
    typeof Symbol !== "undefined" &&
    right[Symbol.hasInstance]
  ) {
    return !!right[Symbol.hasInstance](left);
  } else {
    return left instanceof right;
  }
}

function _classCallCheck(instance, Constructor) {
  if (!_instanceof(instance, Constructor)) {
    throw new TypeError("Cannot call a class as a 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);
  }
}

function _createClass(Constructor, protoProps, staticProps) {
  if (protoProps) _defineProperties(Constructor.prototype, protoProps);
  if (staticProps) _defineProperties(Constructor, staticProps);
  return Constructor;
}

function _defineProperty(obj, key, value) {
  if (key in obj) {
    Object.defineProperty(obj, key, {
      value: value,
      enumerable: true,
      configurable: true,
      writable: true,
    });
  } else {
    obj[key] = value;
  }
  return obj;
}

var Father = /*#__PURE__*/ (function () {
  function Father() {
    _classCallCheck(this, Father);

    _defineProperty(this, "name", "iwen");

    _defineProperty(this, "age", 18);
  }

  _createClass(
    Father,
    [
      {
        key: "run",
        value: function run() {},
      },
      {
        key: "run2",
        value: function run2() {},
      },
    ],
    [
      {
        key: "talk",
        value: function talk() {},
      },
    ]
  );

  return Father;
})();

_defineProperty(Father, "age2", 19);

var Child = /*#__PURE__*/ (function (_Father) {
  _inherits(Child, _Father);

  var _super = _createSuper(Child);

  function Child() {
    _classCallCheck(this, Child);

    return _super.apply(this, arguments);
  }

  return Child;
})(Father);

在線查看

其中類相關的代碼我們之前已經解釋過了,這裏只對繼承相關的代碼的解釋。

_typeof(obj)
判斷傳入對象類型,如果Symbol存在使用typeof,如果不存在,判斷是否爲Symbol的實例,否則依然用typeof判斷。

_inherits(subClass, superClass)    
實現繼承的關鍵步驟,主要是原型鏈相關的設置,傳入子類和父類,將子類原型對象指向父類原型對象,將父類的constructor指向子類,並通過_setPrototypeOf設置子類的原型鏈引用。

_setPrototypeOf(o, p)
如果Object.setPrototypeOf存在,則使用Object.setPrototypeOf設置原型鏈引用,不存則使用__proto__。

_createSuper(Derived)
實現繼承的關鍵步驟,主要是生成了一個result對象,這個對象繼承了父類上屬性和方法,之後會通過apply方法將子類的this指向這個對象,具體解釋已在代碼中註釋:

function _createSuper(Derived) {
  // 判斷是否支持反射
  var hasNativeReflectConstruct = _isNativeReflectConstruct();
  return function () {
    // 獲取父類
    var Super = _getPrototypeOf(Derived),
      result;
    if (hasNativeReflectConstruct) {
        // 這裏的this通過後面的apply方法指向了創建的實例對象
        // _getPrototypeOf(this) 其實就是實例 (new Child()).__proto__ === Child.prototype
      	// NewTarget其實就是子類的構造函數,具體參考_inherits 方法第5行
        var NewTarget = _getPrototypeOf(this).constructor; 
      // Reflect.construct 可以拆分兩步,1. const o = new Super(arguments);  2. o.prototype = NewTarget;
      result = Reflect.construct(Super, arguments, NewTarget);
    } else {
      result = Super.apply(this, arguments);
    }
    // 判斷 result 的類型
    return _possibleConstructorReturn(this, result);
  };
}

**_possibleConstructorReturn(self, call)、_assertThisInitialized(self)
如果result是對象或者函數時返回result,否則判斷this,如果this是undefined,報錯,否則返回。這裏有個小知識點,通過 self === void 0 判斷this是否爲undefined,主要是爲了防止非嚴格模式下undefined被重寫。

**_isNativeReflectConstruct()    **
判斷瀏覽器是否支持反射。

其他都是之前講過的內容,這裏就不在重複了,以上就是實現繼承用到的所有工具方法了,之後就是轉化後的代碼,明白了上面的工具方法,下面的代碼就很好理解了。

var Child = /*#__PURE__*/ (function (_Father) {
  // 繼承父類的原型對象和原型鏈
  _inherits(Child, _Father);
	
  // 創建一個父類的實例
  var _super = _createSuper(Child);

  function Child() {
    _classCallCheck(this, Child);
		
    // 將父類實例中的this指向子類
    return _super.apply(this, arguments);
  }

  return Child;
})(Father);

以上就是babel實現繼承的所有代碼,雖然日常工作中不再需要我們手動實現類和繼承了,但是通過對babel轉義class和extends的代碼解析,讓我們對babel運行原理有一個基本認識,同時也溫習了以前實現類和繼承的相關知識。如果要手動實現一個完美的類和繼承上面就是最佳實踐了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章