- class的寫法及繼承
JavaScript 語言中,生成實例對象的傳統方法是通過構造函數。下面是一個例子
function Point(x, y) {
this.x = x;
this.y = y;}
Point.prototype.toString = function () {
return '(' + this.x + ', ' + this.y + ')';};
var p = new Point(1, 2);
this.x = x;
this.y = y;}
Point.prototype.toString = function () {
return '(' + this.x + ', ' + this.y + ')';};
var p = new Point(1, 2);
上面這種寫法跟傳統的面向對象語言(比如 C++ 和 Java)差異很大,很容易讓新學習這門語言的程序員感到困惑。
ES6 提供了更接近傳統語言的寫法,引入了 Class(類)這個概念,作為對象的模板。通過class
關鍵字,可以定義類。
基本上,ES6 的
class
可以看作只是一個語法糖,它的絕大部分功能,ES5 都可以做到,新的
class
寫法只是讓對象原型的寫法更加清晰、更像面向對象編程的語法而已。上面的代碼用 ES6 的
class
改寫,就是下面這樣。
//定義類
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}}
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}}
上面代碼定義了一個“類”,可以看到里面有一個
constructor
方法,這就是構造方法,而
this
關鍵字則代表實例對象。也就是說,ES5 的構造函數
Point
,對應 ES6 的
Point
類的構造方法
oint
類除了構造方法,還定義了一個
toString
方法。注意,定義“類”的方法的時候,前面不需要加上
function
這個關鍵字,直接把函數定義放進去了就可以了。另外,方法之間不需要逗號分隔,加了會報錯。
ES6 的類,完全可以看作構造函數的另一種寫法
class Point {
// ...
}
typeof Point // "function"
// ...
}
typeof Point // "function"
上面代碼表明,類的數據類型就是函數,類本身就指向構造函數
類的屬性名,可以采用表達式。
let methodName = 'getArea';
class Square {
constructor(length) {
// ...
}
[methodName]() {
// ...
}}
class Square {
constructor(length) {
// ...
}
[methodName]() {
// ...
}}
上面代碼中,
Square
類的方法名
getArea
,是從表達式得到的。
類內部,默認就是嚴格模式,所以不需要使用
use strict
指定運行模式。只要你的代碼寫在類或模塊之中,就只有嚴格模式可用。
考慮到未來所有的代碼,其實都是運行在模塊之中,所以 ES6 實際上把整個語言升級到了嚴格模式。
constructor 方法
constructor
方法是類的默認方法,通過new
命令生成對象實例時,自動調用該方法。一個類必須有constructor
方法,如果沒有顯式定義,一個空的constructor
方法會被默認添加。
class Point {}
// 等同於
class Point {
constructor() {}}
// 等同於
class Point {
constructor() {}}
上面代碼中,定義了一個空的類
Point
,JavaScript 引擎會自動為它添加一個空的
constructor
方法。
constructor
方法默認返回實例對象(即
this
)
類必須使用
new
調用,否則會報錯。這是它跟普通構造函數的一個主要區別,后者不用
new
也可以執行。
class Foo {
constructor() {
return Object.create(null);
}}
Foo()
// TypeError: Class constructor Foo cannot be invoked without 'new'
constructor() {
return Object.create(null);
}}
Foo()
// TypeError: Class constructor Foo cannot be invoked without 'new'
生成類的實例對象的寫法,與 ES5 完全一樣,也是使用
new
命令。前面說過,如果忘記加上
new
,像函數那樣調用
Class
,將會報錯。
class Point {
// ...
}
// 報錯
var point = Point(2, 3);
// 正確
var point = new Point(2, 3);
// ...
}
// 報錯
var point = Point(2, 3);
// 正確
var point = new Point(2, 3);
類不存在變量提升(hoist),這一點與 ES5 完全不同。
new Foo(); // ReferenceError
class Foo {}
class Foo {}
- 類的繼承
Class 可以通過
extends
關鍵字實現繼承 這比 ES5 的通過修改原型鏈實現繼承,要清晰和方便很多。
class Point {}
class ColorPoint extends Point {}
class ColorPoint extends Point {}
上面代碼定義了一個
ColorPoint
類,該類通過
extends
關鍵字,繼承了
Point
類的所有屬性和方法。但是由於沒有部署任何代碼,所以這兩個類完全一樣,等於復制了一個
Point
類。下面,我們在
ColorPoint
內部加上代碼。
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // 調用父類的constructor(x, y)
this.color = color;
}
toString() {
return this.color + ' ' + super.toString(); // 調用父類的toString()
}}
constructor(x, y, color) {
super(x, y); // 調用父類的constructor(x, y)
this.color = color;
}
toString() {
return this.color + ' ' + super.toString(); // 調用父類的toString()
}}
上面代碼中,
constructor
方法和
toString
方法之中,都出現了
super
關鍵字,它在這里表示父類的構造函數,用來新建父類的
this
對象。
ES6 要求,子類的構造函數必須執行一次
super
函數
子類必須在
constructor
方法中調用super
方法,否則新建實例時會報錯。這是因為子類自己的this
對象,必須先通過父類的構造函數完成塑造,得到與父類同樣的實例屬性和方法,然后再對其進行加工,加上子類自己的實例屬性和方法。如果不調用super
方法,子類就得不到this
對象。
class Point { /* ... */ }
class ColorPoint extends Point {
constructor() {
}}
let cp = new ColorPoint(); // ReferenceError
class ColorPoint extends Point {
constructor() {
}}
let cp = new ColorPoint(); // ReferenceError
上面代碼中,
ColorPoint
繼承了父類
Point
,但是它的構造函數沒有調用
super
方法,導致新建實例時報錯。
需要注意的地方是,在子類的構造函數中,只有調用
super
之后,才可以使用this
關鍵字,否則會報錯。這是因為子類實例的構建,是基於對父類實例加工,只有super
方法才能返回父類實例
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}}
class ColorPoint extends Point {
constructor(x, y, color) {
this.color = color; // ReferenceError
super(x, y);
this.color = color; // 正確
}}
constructor(x, y) {
this.x = x;
this.y = y;
}}
class ColorPoint extends Point {
constructor(x, y, color) {
this.color = color; // ReferenceError
super(x, y);
this.color = color; // 正確
}}
上面代碼中,子類的
constructor
方法沒有調用
super
之前,就使用
this
關鍵字,結果報錯,而放在
super
方法之后就是正確的。