前言
對於javascript來說,類別是一種可選(而不是必須)的設計模式,而且在JavaScript這樣的[[Prototype]] 語言中實現類別是很蹩腳的。
這種蹩腳的感覺不只是來自文法,雖然文法是很重要的原因。 js裡面有許多語法的缺點:繁瑣雜亂的.prototype 引用、試圖調用原型鏈上層同名函數時的顯式偽多態以及不可靠、不美觀而且容易被誤解成“構造函數”的.constructor。
除此之外,類別設計其實還存在更進一步的問題。傳統以類別為導向的語言中父類別和子類別、子類別和實例之間其實是複製操作,但是在[[Prototype]] 中並沒有複製。
物件關聯程式碼和行為委託使用了[[Prototype]] 而不是將它藏起來,對比其簡潔性可以看出,類別並不適用於JavaScript。
ES6中CLASS的使用
javascript傳統做法是當產生一個物件實例,需要定義建構函數,然後透過new的方式完成。
function StdInfo(){ this.name = "job"; this.age = 30; } StdInfo.prototype.getNames = function (){ console.log("name:"+this.name); } //得到一个学员信息对象 var p = new StdInfo()
javacript中只有對象,沒有類別。它是基於原型的語言,原型物件是新物件的模板,它將自身的屬性共享給新物件。這樣的寫法和傳統物件導向語言差異很大,很容易讓新手感到困惑。
定義類
到了ES6添加了類,作為對象的模板。以class定義一個類別:
//定义类 class StdInfo { constructor(){ this.name = "job"; this.age = 30; } //定义在类中的方法不需要添加function getNames(){ console.log("name:"+this.name); } } //使用new的方式得到一个实例对象 var p = new StdInfo();
上面的寫法更清晰、更像物件導向程式設計的語法,看起來也更容易理解。
定義的類別只是語法糖,目的是讓我們用更簡潔明了的語法創建物件及處理相關的繼承。
//定义类 class StdInfo { //... } console.log(typeof StdInfo); //function console.log(StdInfo === StdInfo.prototype.constructor); //true
從上面的測試中可以看出來,類別的類型就是一個函數,是一個“特殊函數”,指向的是構造函數。
函數的定義方式有函數宣告和函數表達式兩種,類別的定義方式也有兩種,分別是:類別宣告和類別表達式。
類別宣告
類別宣告是定義類別的一種方式,使用關鍵字class,後面跟上類別名稱,然後就是一對大括號。把這一類需要定義的方法放在大括號中。
//定义类,可以省略constructor class StdInfo { getNames(){ console.log("name:"+this.name); } } // ------------------------------------- //定义类,加上constructor class StdInfo { //使用new定义实例对象时,自动调用这个函数,传入参数 constructor(name,age){ this.name = name; this.age = age; } getNames(){ console.log("name:"+this.name); } } //定义实例对象时,传入参数 var p = new StdInfo("job",30)
constructor是預設方法,使用new定義實例物件時,自動執行constructor函數,傳入所需的參數,執行完constructor後自動傳回實例物件。
一個類別中只能有一個constructor函數,定義多個會報錯。
constructor中的this指向新建立的實例對象,並利用this往新建立的實例物件擴充屬性。
在定義實例物件時,不需要在初始化階段做一些事,可以不用顯示的寫constructor函數。如果沒有明確定義,一個空的constructor方法會被預設添加,constructor(){}
類別表達式
類別表達式是定義類別的另一種形式,類似於函數表達式,把一個函數當作值賦給變數。可以把定義的類別賦值給一個變量,這時候變數就為類別名稱。 class關鍵字之後的類別名稱可有可無,如果存在,則只能在類別內部使用。
定義類別class後面有類別名稱:
const People = class StdInfo { constructor(){ console.log(StdInfo); //可以打印出值,是一个函数 } } new People(); new StdInfo(); //报错,StdInfo is not defined;
定義類別中沒有類別名稱:
const People = class { constructor(){ } } new People();
立即執行的類,在類前要加上new。 p為類別的實例物件。
不存在變數提升
定義類別不存在變數提升,只能先定義類別後使用,跟函數宣告有區別的。
const p = new class { constructor(name,age){ console.log(name,age); } }("job",30)
EXTENDS繼承
使用extends關鍵字實現類別之間的繼承。這比在ES5中使用繼承要方便很多。
//-----函数声明------- //定义前可以先使用,因为函数声明提升的缘故,调用合法。 func(); function func(){} //-----定义类--------------- new StdInfo(); //报错,StdInfo is not defined class StdInfo{}
使用繼承的方式,子類別就擁有了父類別的方法。
如果子類別中有constructor建構函數,則必須使用呼叫super。
//定义类父类 class Parent { constructor(name,age){ this.name = name; this.age = age; } speakSometing(){ console.log("I can speek chinese"); } } //定义子类,继承父类 class Child extends Parent { coding(){ console.log("coding javascript"); } } var c = new Child(); //可以调用父类的方法 c.speakSometing(); // I can speek chinese
子類別必須在constructor方法中呼叫super方法,否則新建實例時會報錯(this is not defined)。這是因為子類別沒有自己的this對象,而是繼承父類別的this對象,然後對其進行加工。如果不呼叫super方法,子類別就得不到this物件。