ES6提出了類(Class)的概念,讓對象的原型的寫法更像面嚮對象語言寫法。 ES6中經過class定義對象,默認具備constructor方法和自定義方法,可是包含在class中的方法不可枚舉。app
class Point{ constructor(){ this.x=x; this.y=y; } toString(){ return this.x+this.y; } }
注意:constructor方法對應ES5的構造函數;建立類的方法不須要使用function關鍵字,方法之間不須要逗號分隔。函數
ES5中定義對象時組合使用構造函數模式和原型模式,構造函數模式用於定義實例屬性,原型模式用於定義共享方法和共享屬性,節省內存,並且支持向構造函數傳遞參數。this
function Point (x,y){ this.x=x; this.y=y; } Point.prototype.toString= function(){ return this.x+this.y; }
1)ES5中的Person構造函數和Person原型對象以及實例Person一、Person2的關係:構造函數的prototype屬性以及實例的__proto__屬性指向原型對象,原型對象的constructor屬性指向構造函數。
spa
構造函數的原型(prototype)屬性在ES6中依舊存在,這一點和ES5同樣:類的方法都定義在類的原型(prototype)上,在類的實例上面調用方法,其實就是調用原型上的方法。prototype
//實例的constructor方法就是類的原型(prototype)的constructor方法 class B{ constructor(){} } let b = new B(); console.log(b.constructor === B.prototype.constructor);//true
2)ES6的類能夠看作是構造函數的另外一種寫法(和ES5中構造函數等同的並非constructor方法):類是function類型,且類自己指向類的prototype對象的constructor屬性,這與ES5同樣。code
class Point{ // ... } console.log(typeof Point) // "function" console.log(Point === Point.prototype.constructor) // true
3)使用類建立實例對象也是直接對類使用new命令,跟ES5中構造函數的用法一致。對象
4)類內部定義的方法都是不可枚舉的,這和ES5不一樣。
blog
5)constructor方法
一個類必須有constructor方法,若是沒有就默認添加constructor(){}。雖然類是函數,可是和ES5不一樣,不經過new而直接調用類會致使類型錯誤,也就是說這個函數僅當在和new一塊兒使用時纔有意義。
6)類的實例
與ES5同樣,實例的屬性除非顯式定義在其自己(即定義在this對象上),不然都是定義在原型上(即定義在class上)。繼承
class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; } } var point = new Point(2, 3); point.toString() // (2, 3) point.hasOwnProperty('x') // true point.hasOwnProperty('y') // true point.hasOwnProperty('toString') // false point.__proto__.hasOwnProperty('toString') // true
以上代碼中,x和y都是實例對象point自身的屬性(由於定義在this變量上),因此hasOwnProperty方法返回true,而toString是原型對象的屬性(由於定義在Point類上),因此hasOwnProperty方法返回false。這些都與ES5的行爲保持一致。圖片
與ES5同樣,類的全部實例共享一個原型對象。
var p1 = new Point(2,3); var p2 = new Point(3,2); p1.__proto__ === p2.__proto__ //true
能夠經過實例的__proto__屬性爲Class原型添加方法,增長後全部的原型都具備這個方法,這和ES5同樣。
var p1 = new Point(2,3); var p2 = new Point(3,2); p1.__proto__.printName = function () { return 'Oops' }; p1.printName() // "Oops" p2.printName() // "Oops"
7)Class不存在變量提高(hoist),這一點與ES5徹底不一樣。
new Foo(); // ReferenceError class Foo {}
上面代碼中,Foo類使用在前,定義在後,這樣會報錯,由於ES6不會把類的聲明提高到代碼頭部。這種規定的緣由與繼承有關,必須保證子類在父類以後定義。
8)嚴格模式
類和模塊的內部,默認就是嚴格模式,因此不須要使用use strict指定運行模式。只要你的代碼寫在類或模塊之中,就只有嚴格模式可用。考慮到將來全部的代碼,其實都是運行在模塊之中,因此ES6實際上把整個語言升級到了嚴格模式。
Class經過extends關鍵字實現繼承,這和ES5經過修改原型鏈實現繼承不一樣(巧合的是,SASS也經過@extend實現樣式繼承):
class ColorPoint extends Point{}
子類必須在constructor方法中調用super方法,不然新建實例時會報錯。這是由於子類沒有本身的this對象,而是繼承父類的this對象,而後對其進行加工。若是不調用super方法,子類就得不到this對象。
class ColorPoint extends Point { constructor(x, y, color) { super(x, y); // 調用父類的constructor(x, y) this.color = color; } toString() { return this.color + ' ' + super.toString(); // super表明父類原型,調用父類的toString() } }
上面代碼中,constructor方法和toString方法之中,都出現了super關鍵字,它在這裏表示父類的構造函數,用來新建父類的this對象。
super這個關鍵字,既能夠看成函數使用,也能夠看成對象使用。第一種狀況,super做爲函數調用時,表明父類的構造函數,只能用在子類的構造函數中。ES6 要求,子類的構造函數必須執行一次super函數。第二種狀況,super做爲對象時,指代父類的原型對象。
ES5的繼承,實質是先創造子類的實例對象this,而後再將父類的方法添加到this上面(Parent.apply(this))。(ES5經過原型模式的繼承:建立子類,而後將父類的實例賦值給子類原型,也就是重寫子類原型,代之以一個新類型的實例。)ES6的繼承機制徹底不一樣,實質是先創造父類的實例對象this(因此必須先調用super方法),而後再用子類的構造函數修改this。
若是子類沒有定義constructor方法,這個方法會被默認添加。在子類的構造函數中,只有調用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; // 正確 } }
和ES5同樣,經過子類建立的實例是父類以及子類的實例:
let cp = new ColorPoint(25, 8, 'green'); cp instanceof ColorPoint // true cp instanceof Point // true
ES5中每個對象都有__proto__屬性,指向對應的構造函數的prototype屬性。ES6中沒有構造函數,Class做爲構造函數的語法糖,同時有prototype屬性和__proto__屬性,所以同時存在兩條繼承鏈。
(1)子類的__proto__屬性,表示構造函數的繼承,老是指向父類。
(2)子類prototype屬性的__proto__屬性,表示方法的繼承,老是指向父類的prototype屬性。
這樣的結果是由於,類的繼承是按照下面的模式實現的。
class A { } class B { } // B的實例繼承A的實例 Object.setPrototypeOf(B.prototype, A.prototype); const b = new B(); // B的實例繼承A的靜態屬性 Object.setPrototypeOf(B, A); const b = new B();
這兩條繼承鏈,能夠這樣理解:做爲一個對象,子類(B)的原型(__proto__屬性)是父類(A);做爲一個構造函數,子類(B)的原型(prototype屬性)是父類的實例。
Object.getPrototypeOf方法能夠用來從子類上獲取父類。
Object.getPrototypeOf(ColorPoint) === Point // true
所以,可使用這個方法判斷,一個類是否繼承了另外一個類。
子類實例的__proto__屬性的__proto__屬性,指向父類實例的__proto__屬性。也就是說,子類實例的原型的原型,是父類實例的原型。
var p1 = new Point(2, 3); var p2 = new ColorPoint(2, 3, 'red'); p2.__proto__ === p1.__proto__ // false p2.__proto__.__proto__ === p1.__proto__ // true
總的來講,ES6是對ES5的形式上的改變,真實內容依舊不變,本質上依舊是經過原型鏈實現繼承。