这一章,我将结合babel的转化,来讲解super的用法
super关键字,有两种用法,要么作为函数调用,要么作为对象使用(深入理解ES6中super关键字(super作为对象使用))。
super作为函数使用
当super作为函数使用时,只能在子类的构造函数中使用,否则,会抛出一个错误。同时,在子类的构造函数中,必须要super(...args),并且super(...args),必须放在在使用this之前。如果类中没有写constructor函数,则会默认加上constructor,如果当前类是子类的话,constructor中的super也会默认加上。
注:子类必须在constructor
方法中调用super
方法,否则新建实例时会报错。这是因为子类自己的this
对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super
方法,子类就得不到this
对象。
下面我将分析类class通过babel转化后的代码,来解释下为什么子类中constructor方法必须要调用super方法:
比如:
class A {}
class B extends A {
constructor(x, y) {
super();
this.x = x;
this.y = y;
}
}
babel转化后:
var A = function A() {
_classCallCheck(this, A);
};
var B = /*#__PURE__*/function (_A) {
_inherits(B, _A);
var _super = _createSuper(B);
function B(x, y) {
var _this;
_classCallCheck(this, B);
_this = _super.call(this);
_this.x = x;
_this.y = y;
return _this;
}
return B;
}(A);
可以看出类class其实质还是构造函数,在每个构造函数A、B中的对实例对象添加实例方法和实例属性时,都必须进行_classCallCheck(this, 类)。
_classCallCheck(this, 类)目的是:判断执行该类的时候,是通过new 还是通过类名直接执行,比如类A,是new A()还是直接A()。
我们可以看到类B通过babel转化后的代码其实是一个立即执行函数function()(),将类A作为参数,传入立即执行函数中,在立即执行函数中主要有三行代码重要,一行:_inherits(B, A),另一行是var _super = _createSuper(B),另一行是:_this = _super.call(this)。
_inherits(B, A)作用:创建两条原型链,一条是以实例对象为开始,另一条是以子类B开始。比如:我们通过let b = new B()new一个实例对象(如果想了解原型链,请进入JavaScript--原型链)
以实例对象开始的一条原型链是(目的:原型方法、原型属性的继承):
另一条是以子类B开始的原型链(目的:类的静态方法上的继承):
_createSuper是一个高阶组件,第一次执行会返回一个函数,作为_super,为啥要将子类作为参数传入呢?因为我们要通过传入的子类,查找子类的原型__proto__(即继承的父类,通过B.__proto__ === A这个理解)。
_createSuper第二次执行,_super.call(this),目的是:通过call方法改变父类构造函数中的this对象为子类中this,执行父类的构造函数,完成子类中this对象的塑造,得到与父类一样的实例对象和方法,并且返回一个对象(这个对象有可能是传入的子类构造函数中的this对象,有可能是父类中构造函数中的返回的对象),之后将这个对象赋值给_this。
在子类B构造函数中,首先会定义一个_this,_this相当于构造函数中的this指针(即实例对象),之后将_super.call(this)返回的结果赋值给_this(得到父类的实例属性和方法),之后在添加子类中实例方法和属性,最后返回_this,作为new的实例对象。
总的来说,其实super(...args),相当于A.apply(this, args) (A是父类,B是子类),不过与babel转化的代码功能唯一区别是:A.apply(this, ...args)的返回值是undefined,而_super.apply(this, args)是有返回值的,如果执行父类中构造函数(A.apply(this, args))有返回值,那_super.apply(this, args)就返回这个,如果A.apply(this, args)没有返回值,那就返回这个this。这也就是为什么我们要在constructor中进行this调用之前要进行super()。
function _createSuper(Derived) {
return function () {
var Super = _getPrototypeOf(Derived), result;
if (_isNativeReflectConstruct()) {
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;
}
下一章,介绍super作为对象使用时情况(深入理解ES6中super关键字(super作为对象使用))