ES6引入了Class(類)這個概念,做爲對象的模板。經過class
關鍵字,能夠定義類。基本上,ES6的class
能夠看做只是一個語法糖,它的絕大部分功能,ES5均可以作到,新的class
寫法只是讓對象原型的寫法更加清晰、更像面向對象編程的語法而已。編程
ES6的類,徹底能夠看做構造函數的另外一種寫法。瀏覽器
類的數據類型就是函數,類自己就指向構造函數。app
使用的時候,也是直接對類使用new
命令,跟構造函數的用法徹底一致。函數
constructor
方法是類的默認方法,經過new
命令生成對象實例時,自動調用該方法。一個類必須有constructor
方法,若是沒有顯式定義,一個空的constructor
方法會被默認添加。類的構造函數,不使用new
是無法調用的,會報錯。這是它跟普通構造函數的一個主要區別,後者不用new
也能夠執行。this
生成類的實例對象的寫法,與ES5徹底同樣,也是使用new
命令。若是忘記加上new
,像函數那樣調用Class
,將會報錯。spa
與ES5同樣,實例的屬性除非顯式定義在其自己(即定義在this
對象上),否則都是定義在原型上(即定義在class
上)。prototype
x
和y
都是實例對象point
自身的屬性(由於定義在this
變量上),因此hasOwnProperty
方法返回true
,而toString
是原型對象的屬性(由於定義在Point
類上),因此hasOwnProperty
方法返回false
。這些都與ES5的行爲保持一致。3d
Class不存在變量提高(hoist),這一點與ES5徹底不一樣。code
上面代碼中,Foo
類使用在前,定義在後,這樣會報錯,由於ES6不會把類的聲明提高到代碼頭部。這種規定的緣由與下文要提到的繼承有關,必須保證子類在父類以後定義。對象
Class的繼承
Class之間能夠經過extends
關鍵字實現繼承,這比ES5的經過修改原型鏈實現繼承,要清晰和方便不少。
上面代碼定義了一個ColorPoint
類,該類經過extends
關鍵字,繼承了Point
類的全部屬性和方法。可是因爲沒有部署任何代碼,因此這兩個類徹底同樣,等於複製了一個Point
類
constructor
方法和toString
方法之中,都出現了super
關鍵字,它在這裏表示父類的構造函數,用來新建父類的this
對象。
子類必須在constructor
方法中調用super
方法,不然新建實例時會報錯。這是由於子類沒有本身的this
對象,而是繼承父類的this
對象,而後對其進行加工。若是不調用super
方法,子類就得不到this
對象。
ES5的繼承,實質是先創造子類的實例對象this
,而後再將父類的方法添加到this
上面(Parent.apply(this)
)。ES6的繼承機制徹底不一樣,實質是先創造父類的實例對象this
(因此必須先調用super
方法),而後再用子類的構造函數修改this
。
另外一個須要注意的地方是,在子類的構造函數中,只有調用super
以後,纔可使用this
關鍵字,不然會報錯。這是由於子類實例的構建,是基於對父類實例加工,只有super
方法才能返回父類實例。
大多數瀏覽器的ES5實現之中,每個對象都有__proto__
屬性,指向對應的構造函數的prototype屬性。Class做爲構造函數的語法糖,同時有prototype屬性和__proto__
屬性,所以同時存在兩條繼承鏈。
(1)子類的__proto__
屬性,表示構造函數的繼承,老是指向父類。
(2)子類prototype
屬性的__proto__
屬性,表示方法的繼承,老是指向父類的prototype
屬性。
上面代碼中,子類B
的__proto__
屬性指向父類A
,子類B
的prototype
屬性的__proto__
屬性指向父類A
的prototype
屬性。
這樣的結果是由於,類的繼承是按照下面的模式實現的。
Object.setPrototypeOf
方法的實現:
故上面代碼等同於這裏的代碼
這兩條繼承鏈,能夠這樣理解:
做爲一個對象,子類(B
)的原型(__proto__
屬性)是父類(A
);做爲一個構造函數,子類(B
)的原型(prototype
屬性)是父類的實例。