主要知識點:類聲明、類表達式、類的重要要點以及類繼承
《深刻理解ES6》筆記 目錄html
JS 在 ES5 及更早版本中都不存在類。與類最接近的是:建立一個構造器,而後將方法指派到該構造器的原型上。這種方式一般被稱爲建立一個自定義類型:編程
function PersonType(name) { this.name = name; } PersonType.prototype.sayName = function() { console.log(this.name); }; let person = new PersonType("Nicholas"); person.sayName(); // 輸出 "Nicholas" console.log(person instanceof PersonType); // true console.log(person instanceof Object); // true
類聲明以 class 關鍵字開始,其後是類的名稱;剩餘部分的語法看起來就像對象字面量中的方法簡寫,而且在方法之間不須要使用逗號:segmentfault
class PersonClass { // 等價於 PersonType 構造器,自有屬性 constructor(name) { this.name = name; } // 等價於 PersonType.prototype.sayName sayName() { console.log(this.name); } } let person = new PersonClass("Nicholas"); person.sayName(); // 輸出 "Nicholas" console.log(person instanceof PersonClass); // true console.log(person instanceof Object); // true console.log(typeof PersonClass); // "function" console.log(typeof PersonClass.prototype.sayName); // "function"
用ES5實現剛纔的類的功能:函數
// 直接等價於 PersonClass let PersonType2 = (function() { "use strict"; //確保在類的內部不能夠重寫類名 const PersonType2 = function(name) { // 確認函數被調用時使用了 new if (typeof new.target === "undefined") { throw new Error("Constructor must be called with new."); } this.name = name; } Object.defineProperty(PersonType2.prototype, "sayName", { value: function() { // 確認函數被調用時沒有使用 new if (typeof new.target !== "undefined") { throw new Error("Method cannot be called with new."); } console.log(this.name); }, //定義爲不可枚舉 enumerable: false, writable: true, configurable: true }); return PersonType2; }());
此例說明了儘管不使用新語法也能實現類的任何特性,但類語法顯著簡化了全部功能的代碼。性能
類與函數有類似之處,即它們都有兩種形式:聲明與表達式。this
//聲明式 class B { constructor() {} } //匿名錶達式 let PersonClass = class { // 等價於 PersonType 構造器 constructor(name) { this.name = name; } // 等價於 PersonType.prototype.sayName sayName() { console.log(this.name); } }; let person = new PersonClass("Nicholas"); person.sayName(); // 輸出 "Nicholas" console.log(person instanceof PersonClass); // true console.log(person instanceof Object); // true console.log(typeof PersonClass); // "function" console.log(typeof PersonClass.prototype.sayName); // "function" //命名錶達式,B能夠在外部使用,而B1只能在內部使用 let PersonClass = class PersonClass2 { // 等價於 PersonType 構造器 constructor(name) { this.name = name; } // 等價於 PersonType.prototype.sayName sayName() { console.log(this.name); } }; console.log(typeof PersonClass); // "function" console.log(typeof PersonClass2); // "undefined",只有在類內部才能夠訪問到
在編程中,能被看成值來使用的就稱爲一級公民( first-class citizen ),意味着它能做爲參數傳給函數、能做爲函數返回值、能用來給變量賦值。
做爲參數傳入函數:spa
function createObject(classDef) { return new classDef(); } let obj = createObject(class { sayHi() { console.log("Hi!"); } }); obj.sayHi(); // "Hi!"
經過當即調用類構造函數能夠建立單例:prototype
//使用 new 來配合類表達式,並在表達式後面添加括號 let person = new class { constructor(name) { this.name = name; } sayName() { console.log(this.name); } }("Nicholas"); person.sayName(); // "Nicholas"
自有屬性須要在類構造器中建立,而類還容許你在原型上定義訪問器屬性:code
class CustomHTMLElement { constructor(element) { this.element = element; } get html() { return this.element.innerHTML; } set html(value) { this.element.innerHTML = value; } } var descriptor = Object.getOwnPropertyDescriptor(CustomHTMLElement.prototype, "html"); console.log("get" in descriptor); // true console.log("set" in descriptor); // true console.log(descriptor.enumerable); // false
非類的等價表示以下:htm
// 直接等價於上個範例 let CustomHTMLElement = (function() { "use strict"; const CustomHTMLElement = function(element) { // 確認函數被調用時使用了 new if (typeof new.target === "undefined") { throw new Error("Constructor must be called with new."); } this.element = element; } Object.defineProperty(CustomHTMLElement.prototype, "html", { enumerable: false, configurable: true, get: function() { return this.element.innerHTML; }, set: function(value) { this.element.innerHTML = value; } }); return CustomHTMLElement; }());
無須使用標識符,而是用方括號來包裹一個表達式:
let methodName = "sayName"; class PersonClass { constructor(name) { this.name = name; } [methodName]() { console.log(this.name); } } let me = new PersonClass("Nicholas"); me.sayName(); // "Nicholas"
訪問器屬性能以相同方式使用需計算的名稱,就像這樣:
let propertyName = "html"; class CustomHTMLElement { constructor(element) { this.element = element; } get [propertyName]() { return this.element.innerHTML; } set [propertyName](value) { this.element.innerHTML = value; } }
你已學會如何在對象字面量上定義一個生成器:只要在方法名稱前附加一個星號( * )。這一語法對類一樣有效,容許將任何方法變爲一個生成器:
class MyClass { *createIterator() { yield 1; yield 2; yield 3; } } let instance = new MyClass(); let iterator = instance.createIterator();
直接在構造器上添加額外方法來模擬靜態成員,這在 ES5 及更早版本中是另外一個通用的模式:
function PersonType(name) { this.name = name; } // 靜態方法 PersonType.create = function(name) { return new PersonType(name); }; // 實例方法 PersonType.prototype.sayName = function() { console.log(this.name); }; var person = PersonType.create("Nicholas");
ES6 的類簡化了靜態成員的建立,只要在方法與訪問器屬性的名稱前添加正式的 static 標註:
class PersonClass { // 等價於 PersonType 構造器 constructor(name) { this.name = name; } // 等價於 PersonType.prototype.sayName sayName() { console.log(this.name); } // 等價於 PersonType.create static create(name) { return new PersonClass(name); } } let person = PersonClass.create("Nicholas");
和普通方法不同的是,static修飾的方法不能在實例中訪問,只能在類中直接訪問。