ES6中通过class实现了类,通过extends实现并标准化了继承,而class、extends本质是语法糖,使用Babel即可将其转为ES5的代码实现。
那么ES6中的类是如何实现的,ES6中继承实现和ES5有什么区别?它又是如何使用ES5实现的继承呢?
首先我们使用Babel,将最简单的ES6类与继承代码转换为ES5,如下:
// ES6代码
class SuperClass {
constructor() {
this.value = 1;
}
toStringSuper() {}
}
class SubClass extends SuperClass {
constructor() {
super();
this.name = 'sub';
}
}
转换后的ES5代码:
// ES5 代码
'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) {
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 }
});
if (superClass)
Object.setPrototypeOf
? Object.setPrototypeOf(subClass, superClass)
: (subClass.__proto__ = superClass);
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError('Cannot call a class as a function');
}
}
var SuperClass = (function() {
function SuperClass() {
_classCallCheck(this, SuperClass);
this.value = 1;
}
_createClass(SuperClass, [
{
key: 'toStringSuper',
value: function toStringSuper() {}
}
]);
return SuperClass;
})();
var SubClass = (function(_SuperClass) {
_inherits(SubClass, _SuperClass);
function SubClass() {
_classCallCheck(this, SubClass);
var _this = _possibleConstructorReturn(
this,
(SubClass.__proto__ || Object.getPrototypeOf(SubClass)).call(this)
);
_this.name = 'sub';
return _this;
}
return SubClass;
})(SuperClass);
一、类与class语法糖
首先,从class开始,我们先来看SuperClass转化出的代码:
// ES5 代码
'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 _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError('Cannot call a class as a function');
}
}
var SuperClass = (function() {
function SuperClass() {
_classCallCheck(this, SuperClass);
this.value = 1;
}
_createClass(SuperClass, [
{
key: 'toStringSuper',
value: function toStringSuper() {}
}
]);
return SuperClass;
})();
代码不难理解,主要分为几个部分:
- SuperClass后面是一个自执行函数,里面定义了SuperClass构造函数,以及执行了为SuperClass添加属性的_createClass函数
- _createClass的功能主要就是使用
Object.defineProperty
为SuperClass的原型对象添加toStringSuper
属性,而且属性的enumerable
为false
。
二、extends与继承
function _possibleConstructorReturn(self, call) {
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 }
});
if (superClass)
Object.setPrototypeOf
? Object.setPrototypeOf(subClass, superClass)
: (subClass.__proto__ = superClass);
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError('Cannot call a class as a function');
}
}
var SuperClass = (function() {
function SuperClass() {
_classCallCheck(this, SuperClass);
this.value = 1;
}
_createClass(SuperClass, [
{
key: 'toStringSuper',
value: function toStringSuper() {}
}
]);
return SuperClass;
})();
var SubClass = (function(_SuperClass) {
_inherits(SubClass, _SuperClass);
function SubClass() {
_classCallCheck(this, SubClass);
var _this = _possibleConstructorReturn(
this,
(SubClass.__proto__ || Object.getPrototypeOf(SubClass)).call(this)
);
_this.name = 'sub';
return _this;
}
return SubClass;
})(SuperClass);
继承的实现主要围绕_inherits
函数来执行:
- 与ES5中寄生组合式继承不同,ES6中继承使用
Object.create()
以SuperClass的原型对象创建了一个新的原型对象,即SubClass.prototype.__proto__ = SuperClass.prototype
。 同时使其constructor属性指向SubClass。 - ES5的继承是子类的原型对象
__proto__
属性指向父类的原型对象,而ES6中继承是子类__proto__
指向父类。
在创建新子类对象时:
- 通过
(SubClass.__proto__ || Object.getPrototypeOf(SubClass)).call(this)
此处的代码执行了父类的构造函数,即:SubClass.__proto__.call(this)
等于SuerClass.call(this)
- 得到
_this
后,再使用子类的constructor
中的内容来修改_this
,最后返回。 - 如果以ES5的继承方式,使用的是
SuperClass.apply(this)
的方式来执行,而ES6是返回新的_this
后再修改,这是两者的不同之处。的是SuperClass.apply(this)
的方式来执行,而ES6是返回新的_this
后再修改,这是两者的不同之处。
最后形成的结构图如下: