潛入理解ES6-類和繼承

ES5中的類

在ES5聲明一個函數(能夠new),再將方法添加到這個方法的原型上,來建立自定義類型。javascript

function Person(name) {
    this.name = name;
}
Person.prototype.sayName = function() {
    console.log(this.name);
};
let person = new Person("xunuo0x");
person.sayName(); // 輸出 "xunuo0x"
console.log(person instanceof Person); // true
console.log(person instanceof Object); // true
複製代碼

ES6中類的聲明

本質:ES5實現方式的語法糖java

咱們拿下面用class聲明的Person爲例:也就是說Person類爲一個具備構造函數行爲的函數,其中內部方法sayName實際上就是Person.prototype.sayName()。因此說本質上是ES5實現方式的語法糖。函數

console.log(typeof Person) // 'function'
複製代碼

區別在於,類的屬性不可從新賦值和不可枚舉的,Person.prototype就是一個只讀屬性。性能

聲明一個類

聲明:ui

  • 構造器:構造器內建立自有屬性
  • 方法:聲明類實例具備的方法
class Person {
    // 等價於 Person 構造器
    constructor(name) {
    this.name = name;
    }
    // 更加簡單的聲明類的內部函數
    // 等價於 Person.prototype.sayName
    sayName() {
        console.log(this.name);
    }
}
let person = new Person("xunuo0x");
person.sayName(); // 輸出 "xunuo0x"
console.log(person instanceof Person); // true
console.log(person instanceof Object); // true
console.log(typeof Person); // "function"
console.log(typeof Person.prototype.sayName); // "function"
複製代碼

class和自定義類型的區別

  • class的聲明不會提高,與let相似
  • class的聲明自動運行於嚴格模式之下
  • class聲明的方法不可枚舉(顯著區別)
  • class的內部方法沒有[[construct]]屬性,沒法new
  • 調用class的構造函數必須new
  • class內部方法不能同名

用ES5重寫以下: 在實現的時候,主要使用Object.defineProperty()實現class內部函數this

// 直接等價於 Person
let Person2 = (function() {
 "use strict";
    // 有個同名的只讀內部函數
    // **類的內部不能修改類名**
    const Person2 = function(name) {
    // 確認函數被調用時使用了 new
    if (typeof new.target === "undefined") {
        throw new Error("Constructor must be called with new.");
    }
    this.name = name;
    }
   
    Object.defineProperty(Person2.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 Person2;
}());
複製代碼

類表達式

  • 匿名類表達式let Person = class{...}
  • 具名類表達式let Person = PersonClass class{...}
  • 區別僅在於class的內部實現時,const PersonClass做爲內部實現的類名

class做爲一級公民

js中能看成值來使用的稱爲一級公民spa

用法:prototype

  • 類名做爲參數傳入函數
  • 當即執行,實現單例模式
// 類名做爲參數傳入函數
function crateObj (ClassName){
    return new ClassName()
}
// 當即執行,實現單例模式
let person = new class {
    constructor (name) {
        this.name = name
    }
    say () {
        console.log(this.name)
    }
}('xunuo0x')
person.say() // "xunuo0x"
複製代碼

class中訪問器屬性

  • get 關鍵字
  • set 關鍵字
  • 內部實現時將getter/setter變量名,經過Object.defineProperty()定義

class中靜態成員

  • static關鍵字
  • 至關於ES5中Person.create() = function() {}
  • 訪問時直接經過類訪問,不能經過實例訪問

使用extends繼承

只要一個表達式能返回具備[[constructor]]就可使用extends繼承;也就是說能夠繼承一個函數code

ES5中的繼承對象

function Parent (name) {
    this.name = name
}
Parent.prototype.sayName = function () {
    console.log(this.name)
}
function Child (name) {
    Parent.call(this, name)
}
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child
複製代碼

在ES6中的繼承

class Parent (name) {
    constructor (name) {
        this.name = name
    }
    sayName() {
        console.log(this.name)
    }
}
class Child extends Parent (name) {
    constructor(name) {
        super(name)
    } 
    // 重寫父類中的方法
    sayName () {
        console.log(`Child ${this.name}`)
    }
}
複製代碼

繼承內置對象

  • ES5中繼承內置對象(如繼承Array可能會產生問題)
  • ES5中繼承,this先被派生類建立
  • ES6中繼承,this先被基類建立,就具備了基類的方法和屬性

Symbol.species屬性

  • extends繼承時,派生類上返回的是派生類的實例
  • 若是想返回基類,能夠設置Symbol.species
class MyClass extends Array {
    static get [Symbol.species]() {
        return this; // 默認返回MyClass類型
        return Array; // 修改返回基類
    }
    constructor(value) {
        this.value = value;
    }
}
複製代碼

new.target

  • 見名知意,就是new操做執行的對象
  • ES6中實例化class時,必需要new,因此在constructor()new.target不多是undefined

mixin繼承

function mixin (...mixin) {
    var base = function() {}
    Object.assign(base, ...mixin)
    return mixin
}

class Person extends mixin(Animal, Monkey) {
    constructor(){
        super(Animal, Monkey)
        // ......
    }
}

複製代碼

小結

  • ES6中class簡化了ES5中的繼承,可是未改變現有的繼承模型。能夠理解爲是ES5基於原型鏈的語法糖
  • 經過class聲明一個類,constructor()做爲構造函數,屬性在constructor()中初始化
  • class內能夠定義getter/setter訪問器屬性
  • 能夠在class內定義非靜態方法,靜態方法綁定在構造器上
  • 類的全部方法都是不可枚舉的,也符合內部方法
  • 實例化一個class必需要new關鍵字
  • extends實現繼承,子類中調用super()訪問父類構造函數
  • 由於class的實現是基於ES5類模型那一套,本質上和ES5中是同樣的,若是過多使用extends可能還會下降性能
相關文章
相關標籤/搜索