最近在開發中見到了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"
在這個例子中,兩個對象obj1
和obj2
各定義了一個方法。在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繼承)