首先,ES6 的 class
屬於一種“語法糖”,所以只是寫法更加優雅,更加像面對物件的程式設計,其思想和 ES5 是一致的。
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function() {
return '(' + this.x + ',' + this.y + ')';
}
複製程式碼
等同於
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ',' + this.y + ')';
}
}
複製程式碼
其中 constructor
方法是類的建構函式,是一個預設方法,通過 new
命令建立物件例項時,自動呼叫該方法。一個類必須有 constructor
方法,如果沒有顯式定義,一個預設的 consructor
方法會被預設新增。所以即使你沒有新增建構函式,也是會有一個預設的建構函式的。一般 constructor
方法返回例項物件 this
,但是也可以指定 constructor
方法返回一個全新的物件,讓返回的例項物件不是該類的例項。
下面好好分析一下 super
關鍵字的作用:
super
這個關鍵字,既可以當做函式使用,也可以當做物件使用。這兩種情況下,它的用法完全不用。
1. 當做函式使用
class A {}
class B extends A {
constructor() {
super(); // ES6 要求,子類的建構函式必須執行一次 super 函式,否則會報錯。
}
}
複製程式碼
注:在 constructor
中必須呼叫 super
方法,因為子類沒有自己的 this
物件,而是繼承父類的 this
物件,然後對其進行加工,而 super
就代表了父類的建構函式。super
雖然代表了父類 A 的建構函式,但是返回的是子類 B 的例項,即 super
內部的 this
指的是 B,因此 super()
在這裡相當於 ```A.prototype.constructor.call(this, props)``。
class A {
constructor() {
console.log(new.target.name); // new.target 指向當前正在執行的函式
}
}
class B extends A {
constructor {
super();
}
}
new A(); // A
new B(); // B
複製程式碼
可以看到,在 super()
執行時,它指向的是 子類 B 的建構函式,而不是父類 A 的建構函式。也就是說,super()
內部的 this
指向的是 B。
2. 當做物件使用
在普通方法中,指向父類的原型物件;在靜態方法中,指向父類。
class A {
c() {
return 2;
}
}
class B extends A {
constructor() {
super();
console.log(super.c()); // 2
}
}
let b = new B();
複製程式碼
上面程式碼中,子類 B 當中的 super.c()
,就是將 super
當作一個物件使用。這時,super
在普通方法之中,指向 A.prototype
,所以 super.c()
就相當於 A.prototype.c()
。
通過 super
呼叫父類的方法時,super
會繫結子類的 this
。
class A {
constructor {
this.x = 1;
}
s() {
console.log(this.x);
}
}
class B extends A {
constructor {
super();
this.x = 2;
}
m() {
super.s();
}
}
let b = new B();
b.m(); // 2
複製程式碼
上面程式碼中,super.s()
雖然呼叫的是 A.prototytpe.s()
,但是 A.prototytpe.s()
會繫結子類 B 的 this
,導致輸出的是 2,而不是 1。也就是說,實際上執行的是 super.s.call(this)
。
由於繫結子類的 this
,所以如果通過 super
對某個屬性賦值,這時 super
就是 this
,賦值的屬性會變成子類例項的屬性。
class A {
constructor {
this.x = 1;
}
}
class B extends A {
constructor {
super();
this.x = 2;
super.x = 3;
console.log(super.x); // undefined
console.log(this.x); // 3
}
}
let b = new B();
複製程式碼
上面程式碼中,super.x
賦值為 3,這時等同於對 this.x
賦值為 3。而當讀取 super.x
的時候,呼叫的是 A.prototype.x
,但並沒有 x
方法,所以返回 undefined。
注意,使用 super
的時候,必須顯式指定是作為函式,還是作為物件使用,否則會報錯。
class A {}
class B extends A {
constructor() {
super();
console.log(super); // 報錯
}
}
複製程式碼
上面程式碼中,console.log(super);
的當中的 super
,無法看出是作為函式使用,還是作為物件使用,所以 JavaScript 引擎解析程式碼的時候就會報錯。這是,如果能清晰的表明 super
的資料型別,就不會報錯。
最後,由於物件總是繼承其他物件的,所以可以在任意一個物件中,使用 super
關鍵字。
結語:
ES6 的 class 畢竟是一個“語法糖”,所以只要理解了 JavaScript 中物件的概念和麵向物件的思想,class 就不難理解啦。