你真的瞭解[super ]關鍵字嗎?
前言
此篇文章是看了阮老師的es6教程,看到super
關鍵字的時候覺得有必要總結梳理一下,原文還是參考 ECMAScript 6入門。
正文
super
這個關鍵字,既可以當作函式使用,也可以當作物件使用。
1.當作函式使用
super
作為函式呼叫時,代表父類的建構函式。ES6
要求 ,子類的建構函式必須執行一次super
函式。
即作為 函式
使用時下面的程式碼時 固定使用套路
:
class A {
constrctor(){
}
}
class B extends A {
constructor() {
super();
}
}
複製程式碼
上面程式碼中的super()代表的誰??
子類 B
的建構函式中的super()
,代表 B
呼叫父類 A
的建構函式執行。
注意⚠️: 雖然這裡的 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
。
還有一點需要注意 ⚠️ :
作為函式時,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.1 super作為物件在普通方法中應用
上程式碼:
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
指向父類的原型物件。
⚠️注意點 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⚠️: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⚠️:通過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.2 super作為物件在靜態方法中應用
如果 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 ⚠️
在子類的靜態方法中通過 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
指向當前的子類,而不是子類的例項。
總之
super
作為函式,super()
代表的就是父類建構函式,裡面的this
就是類的例項。super
作為物件,在普通的方法中super.
的語法指向的是父類的原型物件,在靜態方法中使用super.
語法的話就去父類的靜態方法中找就行了。至於this
指向問題,記住一點:如果在一個方法前,加上static
關鍵字,就表示該方法不會被例項繼承,而是直接通過類來呼叫,所以靜態方法中的this
只會指類而不是例項。