你真的瞭解[super ]關鍵字嗎?
此篇文章是看了阮老師的es6教程,看到super
關鍵字的時候覺得有必要總結梳理一下,原文還是參考ECMAScript 6入門。
正文
super
這個關鍵字,既可以當作函式使用,也可以當作物件使用。
1.當作函式使用
super
作為函式呼叫時,代表父類的建構函式。ES6
要求 ,子類的建構函式必須執行一次super
函式。
即作為函式
使用時下面的程式碼時固定使用套路
:
class A { constrctor(){ } } class B extends A { constructor() { super(); } } 複製程式碼
上面程式碼中的super()代表的誰??
子類B
的建構函式中的super()
,代表B
呼叫父類A
的建構函式執行。
注意:warning:: 雖然這裡的super()
代表了的 父類A
的建構函式,但是返回的卻是 子類B
的例項,即super()
內部的this 指的是 B, 因此super()
在這裡相當於A.prototype.constructor.call(this)
。
用一個大栗子來證明我的論點:
class A { constrctor(){ console.log(new.target.name) } } class B extends A{ constructor(){ super() } } new A() // A new B() // B 複製程式碼
在這段熱氣騰騰的程式碼中,new.target
指向當前正在執行的函式。
囉嗦一下:
new
是從建構函式生成的例項物件的命令。ES6 為new
命令引入了一個new.target
屬性,該屬性一般用在建構函式之中,返回new命令作用於的那個建構函式。
可以看到,在super()
執行時,它指向的是子類B
的建構函式,而不是父類A
的建構函式。即super()
內部的 this 指向的是B
。
還有一點需要注意:warning: :
作為函式時,super()只能用在子類的建構函式中,用在其他地方報錯。
// 錯誤寫法 class A {} class B extends A { m() { super(); // 報錯 } } 複製程式碼
總結一下:
當super 作為函式的時候需要注意以下三點:
-
子類的建構函式必須執行一次
super
函式。
// 再次重申,這是固定寫法 class A { constructor() {} } class B extends A { constructor() { super();// 這裡表示 A.constructor() } } 複製程式碼
-
子類的建構函式中的
super()
代表的是 子類呼叫父類的建構函式執行,這時候super()
中的this,即父類.constructor
中的this
,指向的是子類。
3.super()只能用在子類的建構函式之中,用在其他地方就會報錯。
2.super作為物件時
super
作為物件時,在普通方法中,指向父類的原型物件,在靜態方法中指向父類。
2.1super作為物件在普通方法中應用
上程式碼:
class A { p(){ return 2 } } class B extends A { constrctor(){ super(); console.log(super.p());//2 } } let b = new B(); 複製程式碼
我們發現 在子類B
中super.p()
執行結果為2
。我們猜測這裡的supsuper.p === A.prototype.p
,驗證一下發現結果是true
:
class A { p() { return 2; } } class B extends A { constructor() { super(); console.log(super.p===A.prototype.p); // true } } let b = new B(); 複製程式碼
也就是說這個時候super
在普通的方法中,指向的是A.prototype
即子類中的super
指向父類的原型物件。
:warning:注意點 1.
:由於子類中的super
指向父類的原型物件,所以定義在父類例項上的方法或屬性,是無法通過super
呼叫的。
class A { constructor() { this.s = 3; } } A.prototype.x = 2; class B extends A { constructor() { super(); console.log(super.x) // 2這裡可以獲取到父類原型上的x屬性 } get m(){ return super.s } } let b = new B(); console.log(b.m)// undefined不能獲取到定義在父類例項上的s屬性 複製程式碼
通過上面的程式碼我們發現:
子類B
可以通過super
獲取到父類定義在原型上的屬性,但是定義在父類A
的例項上的屬性,無法獲取到。
注意點2:warning::this指向
ES6 規定,在子類普通方法中通過super
呼叫父類的方法時,方法內部的this
指向當前的子類例項。
class A { constructor() { this.x = 1; } print() { console.log(this.x); } } class B extends A { constructor() { super(); this.x = 2; } m() { super.print();// 這裡等同於 A.proptotype.print() } } let b = new B(); b.m() // 2 複製程式碼
上面程式碼中,super.print()
雖然呼叫的是A.prototype.print()
,但是A.prototype.print()
內部的this指向子類B的例項,導致輸出的是2,而不是1。也就是說,實際上執行的是super.print.call(this)
。
注意點3:warning::
通過super
進行賦值操作
由於this
指向子類例項,所以如果通過super
對某個屬性賦值,這時super
就是this
,賦值的屬性會變成子類例項的屬性。
class A { constructor() { this.x = 1; } } class B extends A { constructor() { super(); this.x = 2; super.x = 3;// 此時的 super 就是 b console.log(super.x); // undefined 等用於是 A.prototype.x console.log(this.x); // 3 } } let b = new B(); 複製程式碼
通過這兩段程式碼我們發現了一個問題,當我通過super
取值的時候取的是父類的原型上屬性,但是當我通過super
賦值的時候這時候super
指向的是子類的例項。
總結一下:
通過上面的三個栗子得出,super
作為物件在普通方法中應用時:
-
子類中的
super
指向父類的原型物件,所以定義在父類例項上的方法或屬性,是無法通過super
呼叫的。 -
在子類普通方法中通過
super
呼叫父類的方法時,方法內部的 this 指向當前的子類例項。 - 通過 super 對某個屬性賦值,這時super就是this,賦值的屬性會變成子類例項的屬性。
2.2super作為物件在靜態方法中應用
如果super
作為物件,用在靜態方法之中,這時super
將指向父類,而不是父類的原型物件。
class Parent { static myMethod(msg) { console.log('static', msg); } myMethod(msg) { console.log('instance', msg); } } class Child extends Parent { static myMethod(msg) { // 此時的 super 指的是父類,Parent super.myMethod(msg); } myMethod(msg) { // 普通函式 此時 super 是指 Parent.prototype super.myMethod(msg); } } Child.myMethod(1); // static 1 var child = new Child(); child.myMethod(2); // instance 2 複製程式碼
上面程式碼中,super在靜態方法之中指向父類,在普通方法之中指向父類的原型物件。
注意點1 :warning:
在子類的靜態方法中通過super
呼叫父類的方法時,方法內部的this
指向當前的子類,而不是子類的例項。
class A { constructor() { this.x = 1; } static print() { console.log(this.x); } } class B extends A { constructor() { super(); this.x = 2; } static m() { super.print();// A.print 中的this指向當前的子類 } } B.x = 3; B.m() // 3 複製程式碼
上面程式碼中,靜態方法B.m
裡面,super.print
指向父類的靜態方法。這個方法裡面的this
指向的是B
,而不是B
的例項。
總結一下:
在子類的靜態方法中通過super
呼叫父類的方法時,方法內部的this
指向當前的子類,而不是子類的例項。