【學習筆記】JavaScript中super

最近在開發中見到了super,但是對這塊不是很清楚,所以學習了一下,並進行一個小小的總結,方便自己再度加強記憶,也方便小夥伴們一起查詢。
因爲也是學習過程,如果文中有理解錯誤的地方,請評論區中指正,十分感謝

1.super的概念

在MDN中對super的定義如下:

super關鍵字用於訪問和調用一個對象的父對象上的函數。
super.prop和super[expr]表達式在類和對象字面量任何方法定義中都是有效的。

其實我理解就是super關鍵字可以用在類和對象裏面,並且可用於訪問父類/父對象構造函數和方法

2.super的幾種應用

2.1 super在類中調用構造函數

class Polygon {
    constructor(height, width) {
      this.name = 'Rectangle';
      this.height = height;
      this.width = width;
    }
    static sayName() {
      console.log('Hi, I am a ', this.name + '.');
    }
  }
  
  class Square extends Polygon {
    constructor(length) {
      // ReferenceError,super 需要先被調用!
        this.color = 'red'; 
      
      // 這裏,它調用父類的構造函數的,  作爲Polygon 的 height, width
      super(length, length);
      
      // 在這可以使用this,注意:在使用this之前, 必須先調用super()。
      this.color = 'red';
    }
  }

在這裏要強調的是:在構造函數中使用時,super必須在this之前使用,這點要牢記。

這是因爲:ES6的繼承,需要先創建父類的this,子類調用super繼承父類的this對象,然後再加工。

換句話說,子類沒有自己的this對象,而是繼承父親的this對象,然後進行加工。如果不調用super,子類就得不到this對象。

2.2 super在類中調用靜態方法

class Rectangle {
    static logNbSides() {
      return 'I have 4 sides';
    }
  }
  
  class Square extends Rectangle {
    static logDescription() {
      return super.logNbSides() + ' which are all equal';
    }
  }
  console.log(Square.logDescription()); // 'I have 4 sides which are all equal'

另外,使用super可以調用父類的靜態方法。如同上面例子,可以調用父類的logNbSides靜態方法

2.3 super在對象中調用方法

  var obj1 = {
    method1() {
      console.log("method 1");
    }
  }
  
  var obj2 = {
    method2() {
     super.method1();
    }
  }
  //Object.setPrototypeOf() 將obj2的原型加到obj1上,然後才能夠使用super調用obj1上的method1
  Object.setPrototypeOf(obj2, obj1);
  obj2.method2(); //  "method 1"

在這個例子中,兩個對象obj1obj2各定義了一個方法。在obj2中, 我們使用super調用了obj1中的方法。 注意:使用這種方法需要利用 Object.setPrototypeOf()obj2的原型加到obj1,然後才能夠使用super調用 obj1上的method1。

3. super和this的指向

其實到這裏,對於如何使用super就會有個大致瞭解,在日常開發也能夠用,但是我覺得會用不行,還是得再深入瞭解,所以這裏進一步分析super和this使用有什麼不同。

總結的說,this和super的指向如下:

  • this:指向函數所在的當前對象
  • super:指向當前對象的原型對象

this指向對象,super指向原型對象
接下來看幾個例子:

3.1 super簡單應用

  var obj1 = {
    method1() {
      console.log("method 1");
    }
  }
  
  var obj2 = {
    method2() {
     super.method1();
    }
  }
  //Object.setPrototypeOf() 將obj2的原型加到obj1上,然後才能夠使用super調用obj1上的method1
  Object.setPrototypeOf(obj2, obj1);
  obj2.method2(); //  "method 1"

這個例子在2.3中已經見過了,再度拿出來就是爲了解釋爲什麼要用Object.setPrototypeOf,因爲super是指向當前對象的原型對象,也就是當在obj2中使用super.method1()實際上是調用obj2.prototype.method1(),但是obj2的原型鏈上沒有Method1方法,所以將obj2的原型加到obj1上面,這樣就可以調用到原型鏈上的方法。

3.2 super中this的指向

const obj1 = {
	method:'method1',
	methods(){
		return this.method;
	}
}

const obj2 = {
	method:'method2',
	sayMethods(){
		return super.methods();
	}
}

Object.setPrototypeOf( obj2, obj1 );

console.log(obj2.sayMethods())

想想這個結果是什麼?

答案是:‘method2’

這個結果說明了雖然super指向原型對象的methods方法,但是this還是綁定在當前obj2對象.

再看一個例子:

class obj1{		 
    constructor(x,y) {
        this.x = x;
        this.y = y;
    }    
    name(){
        return this.y
    }    
}

class obj2 extends obj1{
    constructor(x,y){
        super(x,y);
    }   
    name(){
        return this.x
    }    
    task1(){
        return super.name();
    }    
    task2(){
        return this.name();
    }
}

let d = new obj2('hello','world');
console.log(d.task1())	
console.log(d.task2())

結合上面例子,想想這個結果是什麼?

答案是:‘world’ ‘hello’

原因和上面相同:使用super調用父類的name方法,執行return this.y的語句,但是this指向當前obj2對象的name方法,執行return this.x的語句

通過這兩個例子驗證了上面所說的:

  • this:指向函數所在的當前對象,即this指向 obj2
  • super:指向當前對象的原型對象,即使用super可以調用父類/父對象

3.3 super在靜態和普通方法中的指向

先看一個例子,猜猜是什麼結果?

class Rectangle {
    static logNbSides() {
      return 'this is static function';
    }
    logNbSides() {
        return 'this is normal function';
      }
  }
  
  class Square extends Rectangle {
    static logDescription() {
      return super.logNbSides();
    }
    logDescription() {
        return super.logNbSides();
      }
  }

  var description = new Square()
  console.log(Square.logDescription()); 
  console.log(description.logDescription()); 

答案是:‘this is static function’ ‘this is normal function’
這個例子說明: super 在子類靜態方法之中會指向父類的靜態方法,在子類普通方法之中會指向父類的普通方法。

(至於爲什麼description爲什麼沒有使用靜態方法,這是因爲New的實例不能使用靜態方法,只能用普通的方法,詳細請看JS每日一問中第8節 Class中的static繼承)

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