class X extends P {}
是 ES6 引入一个新语法,用来实现类继承,也就是子类继承父类。
那是如何实现的呢?
将代码 转译成 ES5 后,得:
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return self;
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _getPrototypeOf(o) {
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
return o.__proto__ || Object.getPrototypeOf(o);
};
return _getPrototypeOf(o);
}
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
}_createSuper
});
if (superClass) _setPrototypeOf(subClass, superClass);
}
function _possibleConstructorReturn(self, call) {
if (call && (_typeof(call) === "object" || typeof call === "function")) {
return call;
}
return _assertThisInitialized(self);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
var _typeof = function(obj) {
"@swc/helpers - typeof";
return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
};
function _isNativeReflectConstruct() {
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
if (Reflect.construct.sham) return false;
if (typeof Proxy === "function") return true;
try {
Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function() {}));
return true;
} catch (e) {
return false;
}
}
function _createSuper(Derived) {
var hasNativeReflectConstruct = _isNativeReflectConstruct();
return function _createSuperInternal() {
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);
};
}
var X = function(P1) {
"use strict";
_inherits(X, P1);
var _super = _createSuper(X);
function X() {
_classCallCheck(this, X);
return _super.apply(this, arguments);
}
return X;
}(P);
代码比较多,不用急,我们来一点点分析其实现。先从核心代码开始。
核心代码
...
var X = function(P1) {
"use strict";
_inherits(X, P1);
var _super = _createSuper(X);
function X() {
_classCallCheck(this, X);
return _super.apply(this, arguments);
}
return X;
}(P);
《class X {} 的 ES5 解释》中讲解过“类中代码是在严格模式下执行的,并且只能以 new
方式调用”——子类也不例外。
var X = function(P1) {
"use strict";
...
function X() {
_classCallCheck(this, X);
...
}
...
}(P);
那么内部是如何实现子类继承父类的呢?答案是通过 _inherits(subClass, superClass)
方法。
_inherits(subClass, superClass)
_inherits()
是编译器封装的一个方法,用来实现子类继承父类。
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);
}
var X = function(P1) {
"use strict";
_inherits(X, P1);
...
}(P);
subClass
表示子类,superClass
则表示要继承的父类。在 class X extends P {}
场景下,X
对应 subClass
,P
(即编译后的 P1
)对应 superClass
。
我们来看下 _inherits()
内部实现。分两步:父类类型检验以及执行子类继承。
一、父类类型检验
如果 typeof superClass !== "function" && superClass !== null
判断成立,就报错 TypeError
。就是说父类的值只能是函数或 null
。
可能不太好理解,我们将判断换成等价的 !(typeof superClass === "function" || superClass === null)
,就是说当 superClass
值为函数或 null
时,不会报错。
二、执行子类继承
function _inherits(subClass, superClass) {
...
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
writable: true,
configurable: true
}
});
if (superClass) _setPrototypeOf(subClass, superClass);
}
有两条继承关系:
subClass.prototype
(包含不可枚举属性constructor
) 的原型对象指向superClass.prototype
——用于实例属性的继承。- 实现机制:通过
Object.create(proto, propertiesObject)
方法在创建subClass.prototype
的同时,将内部原型对象指向superClass.prototype
。
- 实现机制:通过
subClass
的原型对象指向superClass
——用于类静态属性继承。- 实现机制:
_setPrototypeOf(subClass, superClass)
函数内部调用了Object.setPrototypeOf(subClass, superClass)
/subClass*._*proto__ = superClass
来设置原型
- 实现机制:
两条继承关系通过下图一目了然。
[[[prototype]]
是一个对象内部属性,指向对象原型对象
至此,已经实现了 subClass
到 superClass
的继承关系。
还有一个地方,就是在实例化子类的时候,需要调用父类构造函数。
调用父类构造函数
function _createSuper(Derived) {
var hasNativeReflectConstruct = _isNativeReflectConstruct();
return function _createSuperInternal() {
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);
}
var X = function(P1) {
...
var _super = _createSuper(X);
function X() {
...
return _super.apply(this, arguments);
}
return X;
}(P);
分析下代码执行过程:
_createSuper(X)
会返回一个包装函数(即_super
),函数会在子类X
实例化时执行;new X()
时,执行包装函数_super
,包装函数的内部逻辑是查找并调用当前子类的父类构造函数(P
),然后返回执行结果。
当然,这是在子类没有任何逻辑情况下。当子类中包含逻辑时:
class X extends P {
constructor(a) {
super(a);
this.b = 'b';
}
}
将代码 转译成 ES5 后,核心代码。
var X = function(P1) {
"use strict";
_inherits(X, P1);
var _super = _createSuper(X);
function X(a) {
_classCallCheck(this, X);
var _this;
// 关键代码
_this = _super.call(this, a);
_this.b = "b";
return _this;
}
return X;
}(P);
当子类构造函数中包含逻辑时,会先执行父类构造函数(_this = _super.call(this, a)
),再在返回的对象基础上执行子类构造函数里的逻辑(_this.b = "b"
),并返回最终的实例对象(_this
)。
总结
因此 class X extends P {}
内部执行如下:
- 将
X.prototype
的原型对象设置成P.prototype
- 将
X
的原型对象设置成P
- 执行
P
构造函数逻辑 - 执行
X
构造函数逻辑