接着上一篇文章,我们继续进行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运行原理有一个基本认识,同时也温习了以前实现类和继承的相关知识。如果要手动实现一个完美的类和继承上面就是最佳实践了。