深刻理解ES6筆記(九)JS的類(class)

主要知識點:類聲明、類表達式、類的重要要點以及類繼承
圖片描述

《深刻理解ES6》筆記 目錄html

ES5 中的仿類結構

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"

類聲明和函數聲明的區別和特色

  1. 類聲明不會被提高,這與函數定義不一樣。類聲明的行爲與 let 類似,所以在程序的執行到達聲明處以前,類會存在於暫時性死區內。
  2. 類聲明中的全部代碼會自動運行在嚴格模式下,而且也沒法退出嚴格模式。
  3. 類的全部方法都是不可枚舉的,這是對於自定義類型的顯著變化,後者必須用Object.defineProperty() 才能將方法改變爲不可枚舉。
  4. 類的全部方法內部都沒有 [[Construct]] ,所以使用 new 來調用它們會拋出錯誤。
  5. 調用類構造器時不使用 new ,會拋出錯誤。
  6. 試圖在類的方法內部重寫類名,會拋出錯誤。

用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修飾的方法不能在實例中訪問,只能在類中直接訪問。

相關文章
相關標籤/搜索