接着上一篇文章,我們繼續進行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運行原理有一個基本認識,同時也溫習了以前實現類和繼承的相關知識。如果要手動實現一個完美的類和繼承上面就是最佳實踐了。