深入理解ES6中super关键字(super作为函数使用)

这一章,我将结合babel的转化,来讲解super的用法

super关键字,有两种用法,要么作为函数调用,要么作为对象使用(深入理解ES6中super关键字(super作为对象使用))。

  1. 作为函数使用时,指向父类的构造函数;
  2. 作为对象使用时,在静态方法中,指向父类,在普通方法中,指向的是父类的原型对象;

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.__proto__ === B.prototype

        B.prototype.__proto__ === A.prototype

        A.prototype.__proto__ === Object.prototype

        Object.prototype.__proto__ === null

    另一条是以子类B开始的原型链(目的:类的静态方法上的继承): 

        B.__proto__ === A

        A.__proto__ === Function.prototype

        Function.prototype.__proto__ === Object.prototype

        Object.prototype.__proto__ === null

    _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作为对象使用))

你可能感兴趣的:(ES6--类(class))