其餘章節請看:javascript
es6 快速入門 系列html
類(class)是 javascript 新特性的一個重要組成部分,這一特性提供了一種更簡潔的語法和更好的功能,可讓你經過一個安全、一致的方式來自定義對象類型。java
es5 及早期版本中沒有類的概念,一般會編寫相似下面這樣的代碼來自定義類:es6
// 自定義類的思路是:首先建立一個構造函數,而後定義一個方法並賦值給構造函數的原型 function Rectangle(length, width){ this.length = length; this.width = width; } Rectangle.prototype.getArea = function(){ return this.length * this.width } let rect = new Rectangle(2, 3) console.log(rect.getArea()) // 6 console.log(rect instanceof Rectangle) // true
若是還須要實現繼承,可能會這麼實現:數組
// 定義類:Rectangle function Rectangle(length, width){ this.length = length; this.width = width; } Rectangle.prototype.getArea = function(){ return this.length * this.width } // 定義類:Square function Square(length){ // 對象冒充 Rectangle.call(this, length, length) } // 指定 Square 的原型 Square.prototype = Object.create(Rectangle.prototype, { constructor: { value: Square, enumerable: true, writable: true, configurable: true } }); let square = new Square(3) console.log(square.getArea()) // 9 - getArea() 方法從父類繼承 console.log(square instanceof Square) // true console.log(square instanceof Rectangle) // true
經過本身模擬類、模擬繼承,實現起來比較複雜,也很是容易出錯。安全
es6引入類(class),讓類的建立和繼承都更加容易。下面咱們重寫 Rectangle
的例子:app
// 經過 class 定義一個類 class Rectangle{ constructor(length, width){ this.length = length; this.width = width; } // 方法直接寫在 class 中 getArea(){ return this.length * this.width } } // 經過 extends 關鍵字指定類繼承的函數,原型會自動調整 class Square extends Rectangle{ constructor(length){ // 經過調用 super() 方法便可訪問基類的構造函數 super(length, length) } } let square = new Square(3) console.log(square.getArea()) // 9 console.log(square instanceof Square) // true console.log(square instanceof Rectangle) // true
這個版本比咱們自定義的要簡單不少,若是你先前對其餘語言中的繼承語法有所瞭解,那麼你應該會以爲很親切。函數
有關類(class)的具體使用、其餘特性,均可以在下面的補充章節中找到答案this
類和函數都存在聲明形式和表達式形式。es5
聲明類,首先編寫 class 關鍵字,接着是類的名字,其餘部分的語法相似對象字面量的簡寫形式,但類的各元素之間不須要使用逗號。就像這樣:
// class 類名 class People{ constructor(name){ this.name =name; } // 不須要逗號 sayName(){ console.log(this.name) } } let people = new People('aaron') people.sayName() // aaron console.log(typeof People) // function {1} - People 其實仍是一個函數
類的表達式語法:
// 直接將一個類賦值給變量 People let People = class{ constructor(name){ this.name =name; } sayName(){ console.log(this.name) } } let people = new People('aaron') people.sayName() // aaron
類(class)只是自定義類的一個語法糖。用 class 聲明的類是一個函數,那麼將這個函數賦值給一個變量,固然沒問題。
在程序中,一等公民是指能夠傳入函數,能夠從函數返回,也能夠賦值給變量。javascript 函數是一等公民,這是 javascript 中的一個獨特之處,es6 也把類(class)設計成了一等公民。例如,將類(class)做爲參數傳入函數:
function createObject(classDef){ return new classDef() } let obj = createObject(class People{ sayName(){ console.log('aaron') } }) obj.sayName() // aaron
在」解決方法「章節中,咱們已經學會用 class 建立類,經過 extends 關鍵字指定類繼承的函數,以及使用 super() 調用基類的構造函數。
當使用 super() 時切記如下幾個關鍵點:
class A{} class B extends A{ constructor(){ } } let b = new B() // 報錯 - 沒有調用super()
如今能夠經過繼承建立本身的特殊數組。請看下面:
class MyArray extends Array{ } let myArray = new MyArray() myArray[0] = 11 console.log(myArray.length) // 1 myArray.length = 0; console.log(myArray[0]) // undefined
MyArray 繼承自 Array,其行爲與 Array 也很類似。但在之前,用傳統的繼承方式是沒法實現這樣的功能。es6 類語法的一個目標是支持內建對象繼承,於是 es6 中的類繼承模型與 es5 中的稍有不一樣,主要體現是:
咱們也能夠繼承自其餘內建對象
es5 及更早,經過直接將方法添加到構造函數中來模擬靜態成員,例如:
function People(){} People.create = function(){}
es6 簡化了這個過程,類(class)經過 static 關鍵字定義靜態方法:
class People{ constructor(name){ this.name = name } static create(name){ return new People(name) } } let p1 = People.create('aaron') console.log(p1.name) // aaron
靜態成員也能夠被繼承,請看示例:
class A{ static say(){ console.log('A say') } } class B extends A{ } B.say() // A say
es6 中最強大的一面或許是從表達式中導出類了。只要表達式能夠被解析爲一個函數而且具備[[Constructor]]屬性和原型,就能夠用 extends 派生。舉個例子:
function Rectangle(length, width){ this.length = length; this.width = width; } Rectangle.prototype.getArea = function(){ return this.length * this.width } function getBase(){ return Rectangle; } // getBase()這個表達式返回 Reactangle, 具備[[Constructor]]屬性和原型,所以 Square 類能夠直接繼承它 class Square extends getBase(){ constructor(length){ super(length, length) } } let square = new Square(3) console.log(square.getArea()) // 9
extends 強大的功能使得類能夠繼承自任意類型的表達式,從而建立更多的可能性。
class A{ constructor(){ if(new.target === A){ console.log(true) }else{ console.log(false) } } } class B extends A{ constructor(){ super() } } new A() // true {1} new B() // false {2} - B 調用 A 的構造函數,因此當調用發生時 new.target 等於 B
簡單狀況下,new.target 等於類的構造函數,就像行{1},但有時卻會不一樣(行{2})。理解它(行{2})很是重要,由於每一個構造函數能夠根據自身被調用的方式改變本身的行爲。例如,能夠用 new.target 建立一個抽象基類(不能被直接實例化的類),就像這樣:
// 抽象基類 BaseClass class BaseClass{ constructor(){ if(new.target === BaseClass){ throw new Error('這個類不能被實例化') } } sayName(){ console.log(this.name) } } class B extends BaseClass{ constructor(name){ super() this.name = name } } let b = new B('aaron') b.sayName() // aaron new BaseClass() // Error: 這個類不能被實例化
類支持直接在原型上定義訪問器屬性,例如咱們用訪問器屬性封裝一下數據 name:
// 數據 name 存儲在變量 _name 中,只能經過訪問器屬性來操做,從而達到數據封裝的目的 class People{ constructor(name){ this._name = name; } set name(v){ this._name = v; } get name(){ return this._name } } let people = new People('aaron') console.log(people.name) // aaron people.name = 'lj' console.log(people.name) // lj
類方法和訪問器屬性也支持可計算成員名稱,請看下面示例:
let methodName = 'sayName' let getName = 'age' class People{ constructor(name){ this.name = name; } [methodName](){ console.log(this.name) } get [getName](){ console.log('get') } } let people = new People('aaron') people.sayName() // aaron people.age // get
在類中,能夠將任意方法定義成生成器。但若是你的類是用來表示集合,那麼定義一個默認的迭代器會更有用:
class Collection{ constructor(){ this.items = [] } push(v){ this.items.push(v) } *[Symbol.iterator](){ yield *this.items.values() } } let collection = new Collection() collection.push('11') collection.push('22') collection.push('33') for(let v of collection){ console.log(v) // 11 22 33 }
其餘章節請看: