深入理解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作爲對象使用)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章