在ES6以前,準確來講JavaScript語言並沒有類的概念,卻有模擬類的作法。相比在相似java這類傳統面嚮對象語言中經過類來生成實例,js則經過構造函數模擬類來生成實例。java
這是由於在JS設計初期,做者Brendan Eich選擇使用原型來描述對象而非類,但被管理層要求模仿java,所以引入了new this等語言特性,也就是咱們所使用的構造函數作法。es6
那麼自ES6起,JavaScript正式引入了class關鍵字,自此咱們也能夠經過class來定義類了。ecmascript
但須要清楚的是ES6中class只是構造函數的一種語法糖,並不是新鮮玩意,class能實現的,咱們經過ES5構造函數一樣能夠實現。函數
本篇文章只是ES6入門學習筆記,想了解詳細文檔請閱讀阮一峯大神的 ECMAScript 6 入門,那麼本文開始。學習
1.寫法變化this
在ES6以前,咱們模擬一個類的作法是通構造函數:spa
let Parent = function (name, age) { this.name = name; this.age = age; }; Parent.prototype.sayName = function () { console.log(this.name) }; let child = new Parent('echo', 26); child.sayName();//echo
ES6 class實現更像類的寫法,咱們改寫上面的方法:prototype
class Parent { constructor(name, age) { this.name = name; this.age = age; }; sayName() { console.log(this.name); }; }; let child = new Parent('echo', 26); child.sayName(); //echo
簡單對比下,寫法上主要這幾點變化:設計
1.構造函數名Parent在class寫法時變成了類名,但調用方式不變,依然經過new關鍵字建立實例。code
2.構造函數中this相關操做,在class寫法時概括到了constructor方法中,也就是說ES5的構造函數Parent對應ES6的Parent類中的constructor構造方法。
3.ES5中原型上的方法sayName在ES6 class寫法中直接寫在了內部,同時省略了function關鍵字。
因此對於方法添加,下面這兩種寫法是等效的:
// ES6 class Parent { constructor() {}; sayName() {}; sayAge() {}; }; // ES5 let Parent = function () {}; Parent.prototype = { constructor: function () {}, toString: function () {}, sayAge: function () {} };
能夠看到,ES5寫法同樣能夠一次批量在原型上添加方法,但我發現,class類不能直接這麼作,如下有三種給class類原型添加方法的作法,我分別輸出了它們:
// 寫法一 class Point { constructor() {} toString() {} toValue() {} }; // 寫法二 class Point {}; Point.prototype = { constructor() {}, toString() {}, toValue() {}, }; // 寫法三 class Point {}; Point.prototype = { constructor: function () {}, toString: function () {}, toValue: function () {}, };
經過對比能夠發現,第2、三種寫法先定義class類,再使用prototype在原型上添加方法對於class類無效,方法並無被添加進去。
你確定納悶了,外部添加無效,那爲啥那恰恰有個constructor呢,這是由於constructor方法是class類自帶的。
class Parent {
};
//等同於
class Parent {
constructor() {}
};
也就是說當要給class類原型添加方法時,若是使用ES5的添加作法並不會生效;固然也不是寫在外部就會失效,經過assign方法仍是能夠作到這一點:
class Point {
constructor() {};
};
Object.assign(Point.prototype, {
toString() {},
toValue() {}
});
2.類建立實例必須使用new
經過class類建立實例必須使用new關鍵字,不使用會報錯,這點與構造函數不一樣,在ES5中其實咱們不使用new也能調用構造函數建立實例,雖然這樣作不符合規範。
class Parent { constructor() {} }; let son = Parent();//報錯 let Parent = function (name) { this.name = name; }; let son = Parent();//不符合規範
3.類的內部方法沒法枚舉
最後一點區別是,class類內部定義的方法沒法枚舉,也就是沒法經過Object.keys方法獲取,但在ES5中keys方法是有效的。
class Point { constructor() {}; toString() {}; toValue() {}; }; Object.keys(Point);//[] Object.getOwnPropertyNames(Point.prototype);//['constructor','toString','toValue']
2、constructor方法
前面已經說了,當咱們建立一個類,即使內部沒寫constructor方法,類也會自帶。
因爲類的方法都在原型上,因此當咱們調用實例上的方法等同於調用類原型上的方法:
class Parent { constructor() {} }; let son = new Parent(); console.log(son.sayName === Parent.prototype.sayName);//true
類實例的constructor指向類,這與構造函數實例的constructor指向構造函數自己保持一致:
//類的實例與構造函數實例的constructor屬性都指向建立本身的類或構造函數 class Parent { constructor() {} }; let son = new Parent(); console.log(son.constructor);//class Parent let Parent = function (name) { this.name = name; }; let son = new Parent(); console.log(son.constructor);//Parent
類與構造函數prototype對象的constructor屬性都會指向本身,這點也保持了一致。
//類與構造函數prototype的constructor屬性都指向本身 class Parent { constructor() {} }; console.log(Parent.prototype.constructor === Parent);//true let Parent = function (name) { this.name = name; }; console.log(Parent.prototype.constructor === Parent);//true
其實到這裏,class類基本用法算說完了,下面主要是對於class概念其它補充。
3、class類的其它補充
1.class類中的存值取值函數
在類的內部也可使用get與set方法對某個屬性的取值存值操做作攔截處理。
class Parent { get name() { return 'echo' }; set name(val) { console.log(val); }; }; let son = new Parent(); son.name = '時間跳躍'; //時間跳躍 son.name; //echo
當咱們存值和取值時,其實是調用了class內部的get與set對應方法,這點與ES5保持一致。
2.class內中的屬性名可使用變量
let name = 'sayName'; class Parent { [name]() { console.log('echo'); } }; let son = new Parent(); son[name]();//echo
3.class表達式寫法
let Parent = class { sayName() { console.log('echo'); } }; let son = new Parent(); son.sayName(); //echo
與函數表達式相同,class類在表達式寫法下也能添加一個class名:
let Parent = class Me{ sayName() { console.log('echo'); } }; let son = new Parent(); son.sayName(); //echo console.log(Parent.name);//Me
那麼此時,Parent類的name屬性爲Me,但建立實例你得new Parent,而非new Me();
4.類中this指向
類方法中自帶嚴格模式,且類方法中的this默認指向類的實例
class Parent { sayName() { console.dir(this); //Patent {} this.sayAge(26); }; sayAge(age) { console.log(age, this); } }; let son = new Parent(); son.sayName(); //26 Patent {} console.log(Parent); //class Parent
但若是你在內部方法單獨抽出來在外部調用,this此時會指向undefined(嚴格模式);
class Parent { sayName() { console.log(this);//undefined this.sayAge(26); }; sayAge(age) { console.log(age); } }; let son = new Parent(); let {sayName} = son; sayName();//報錯
解決方式是,能夠經過在class類中的constructor構造方法中直接爲你須要外部調用的方法綁定this。
class Parent { constructor(){ this.sayName = this.sayName.bind(this); } sayName() { this.sayAge(26) }; sayAge(age) { console.log(age); } }; let son = new Parent(); let {sayName} = son; sayName();//26
或者在構造方法中利用箭頭函數,由於箭頭函數中的this指向自身定義時所在的對象,此時箭頭函數this指向實例。
class Parent { constructor() { this.sayName = () => { this.sayAge(26); } } sayAge(age) { console.log(age); } }; let son = new Parent(); let {sayName} = son; sayName(); //26
5.類的靜態方法
我在JavaScript模式一書的讀書筆記中也有提到ES5中的構造函數靜態方法;若是某個方法只有類自身能夠調用,實例並不會繼承,那麼咱們通常稱此方法爲靜態方法。
//ES5 function Parent (){}; Parent.sayAge = function (){ console.log(26); }; Parent.sayAge()//26 let son = new Parent(); son.sayAge()//報錯,找不到這個方法 //ES6 class Parent { static sayAge() { console.log(26); } }; Parent.sayAge()//26 let son = new Parent(); son.sayAge()//報錯,找不到這個方法
在上述代碼中,我分別用ES5與ES6兩種寫法分別爲類(構造函數)添加了靜態方法sayAge。
很明顯這個方法只能被類自身調用,實例沒法繼承,只是相比ES5直接添加在構造函數上,類使用了static字段,也就是說,若是你想讓某個方法做爲靜態方法,請在前面添加static。
另外有一點,若是靜態方法中使用了this,此時this指向了類,而不是實例,請注意。
//ES5 function Parent (){}; Parent.sayAge = function (){ console.log(this); }; Parent.sayAge()//構造函數自身 //ES6 class Parent { static sayAge() { console.log(this); } }; Parent.sayAge()//class Patent 類自身
6.類的靜態屬性
同理,在ES5中,我在構造函數內部某個屬性只想給構造函數自身使用,實例沒法使用,此時就得使用靜態屬性。
//ES5 function Patent(){}; Patent.age = 26; Patent.sayAge = function (){ console.log(this.age); } Patent.sayAge();//26 //ES6 class Parent { static sayAge() { console.log(this.age); }; }; Parent.age = 26; Parent.sayAge();//26
上述代碼中分別用ES5,ES6爲類添加了靜態屬性,其實都是直接加在類上,作法相同。
固然在後面的提案中,也推薦使用static來建立類的靜態屬性,作法與靜態方法相同,也就是說有兩種方法能夠作到這點。
class Parent { static age = 26; static sayAge() { console.log(this.age); }; }; Parent.sayAge();//26
7.實例屬性的簡寫方法
我在前面說,當聲明一個類,即便不寫constructor方法,類也會自帶,那我想省略掉constructor方法,同時還想給實例添加屬性呢怎麼辦,其實也能夠簡寫。
class Parent { constructor() { this.name = 'echo'; this.age = 26; }; sayName() {}; }; let son = new Parent('echo', 26); son.name //echo son.age //26 class Parent { name = 'echo'; age = 'age'; sayName() {}; }; let son = new Parent(); son.name //echo son.age //26
以上兩種寫法等效,第二種實例賦值時直接寫在了類的頂部,同時去掉了this,固然若是實例賦值帶參數,那就無法簡寫了。
最後有一個類的私有屬性和私有方法沒說,由於ES6沒提供,只能模擬,有興趣能夠自行閱讀。
那麼ES6中class類基本用法就說到這裏了。