說明:本文參考阮一峰的ECMAScript 6 入門
1.Class
類的由來
JavaScript語言中,生成執行個體對象的傳統方法是通過構造函數。
function Person (name, age) {
this.name = name
this.age = age
}
Person.prototype.toString = function () {
return `${name},${age}`
}
var per = new Person("wangwu", 19)
上面這種寫法跟面向對象語言(C++、Java)差異很大,很容易讓學習這門語言的程式員感到困惑。
ES6提供了更接近傳統語言的寫法,引入了class(類)這個概念,作為對象的模闆。通過class關鍵字可以定義類。
新的Class寫法隻是讓對象原型的寫法更加清晰、更加面向對象程式設計的文法而已。上面代碼用ES6的class寫法,就是下面這樣。
calss Person {
constructor(name, age){
this.name = name
this.age = age
}
toString() {
return `${this.name},${this.age}`
}
}
var per = new Person("wangwu", 19)
上面代碼,定義了一個類,裡面有一個constructor方法,這就是構造方法,this代表執行個體對象。構造執行個體的時候,也是直接對類使用new指令,跟構造函數的用法完全一緻。
Person類還定義了一個toString方法。注意,定義類的方法時候,不需要在前面加上關鍵字function,直接把函數定義放進去就可以了。另外方法之間,不需要逗号分隔,加了逗号會報錯。
ES6的類可以看作構造函數的另一種寫法。
class Person {
}
typeof Person //function
Person === Point.prototype.constructor //true
上面代碼說明,類的資料類型就是函數,類本身指向構造函數。類的所有方法都是定義在類的prototype屬性上面。在類的執行個體上調用方法就是調用原型上的方法。
另外,類的内部所有定義的方法,都是不可枚舉的(non-enumerable)。
注意點
- 嚴格模式,類和子產品的内部,預設就是嚴格模式。考慮到未來所有的代碼,其實都是運作在子產品之中,是以 ES6 實際上把整個語言更新到了嚴格模式。
- 不存在提升,類不存在變量提升(hoist)。
- this指向,類的方法内部如果含有this,它預設指向類的執行個體。
- 靜态方法,如果在一個方法前,加上static關鍵字,就表示該方法不會被執行個體繼承,而是直接通過類來調用,這就稱為“靜态方法”。如果靜态方法包含this關鍵字,這個this指的是類,而不是執行個體。
2.CLass的繼承
class是通過extends關鍵字來繼承的,這比 ES5 的通過修改原型鍊實作繼承,要清晰和友善很多。
class Person {
}
class Student extends Person {
}
上面代碼定義了一個Student類,該類通過extends關鍵字繼承了Person類的所有屬性和方法。
類的prototyppe屬性和__proto__屬性
大多數浏覽器ES5實作之中,每一個對象都有__proto__屬性,指向對應的構造函數的prototype屬性。Class作為構造函數的文法糖,同時有prototype屬性和__proto__屬性。是以具有兩條繼承鍊。
(1)子類的__proto__屬性,表示構造函數的繼承,總是指向父類。
(2)子類的prototype的__proto__屬性,表示方法的繼承,總是指向父類的prototype屬性。
class A {
}
class B extends A {
}
B.__proto__ === A //true
B.prototype.__proto__ === A.prototype
上面代碼中,子類B的__proto__屬性指向父類A,子類B的prototype屬性的__proto__屬性指向父類A的prototype屬性。
這樣的結果是因為,類的繼承是按照下面的模式實作的。
class A {
}
class B {
}
// B 的執行個體繼承 A 的執行個體
Object.setPrototypeOf(B.prototype, A.prototype);
// B 繼承 A 的靜态屬性
Object.setPrototypeOf(B, A);
const b = new B();
下面這段代碼是Object.setPrototypeOf方法的實作。
Object.setPrototypeOf = function (obj, proto) {
obj.__proto__ = proto;
return obj;
}
是以,就得到了上面的結果。