淺談ES6類


ECMAScript6中終於引入了類的特性,在此之前只能通過其他方法定義並關聯多個相似的對象,當然了,ES6中的類與其他語言中的還是不太一樣,其語法的設計實際上借鑒了JavaScript的動態性,本文檔簡單介紹一下ES6及其新特性。

類的聲明

ES6中聲明一個類,首先編寫class關鍵字,緊跟着是類的名字,其他部分的語法類似於對象字面量方法的簡寫形式,但是不需要子類的各元素之間使用逗號分隔,請看下面這段簡單的類聲明代碼:

    class PersonClass{
        constructor(name){
            this.name = name
        }
        sayName(){
            return this.name
        }
    }

    let person = new PersonClass('xiaoMing')
    console.log(person.sayName())                        // 'xiaoMing'
    console.log(person instanceof PersonType)           //true
    console.log(person instanceof Object)              //true

    console.log(typeof PersonClass)                    // function
    console.log(typeof PersonClass.prototype.sayName) // function

對比類聲明語法定義PersonClass的行為與ES5之前用構造函數構建近似類過程相似,但是仍有很多差異:

1.  函數聲明可以被提升,而類聲明與let聲明類似,不能被提升;真正執行聲明語句之前,他們會一直存在於臨時死區中。  
2.  類聲明中的所有代碼將自動運行在嚴格模式下,而且無法強行讓代碼脫離嚴格模式執行。
3.  在自定義類型,需要通過Object.defineProperty()方法手工指定某個方法為不可枚舉,而在類中,所有的方法都是不可枚舉的。
4.  每個類都有一個名為[Constructor]]的內部方法,通過關鍵字new調用方那些不含[Constructor]]的方法會導致程序拋出錯誤。
5.  使用關鍵字new以外的方式調用類的構造函數會導致程序拋出錯誤。
6.  在類中修改類名會導致程序報錯。

類不僅僅可以通過聲明實現,還可以使用類表達式 ,類表達式又分為匿名類表達式和命名類表達式,具體語法如下所示:

//匿名類表達式
    let PersonClass = class{
        constructor(name){
            this.name = name
        }
        sayName(){
            console.log(this.name)
        }
    }
//命名表達式
    let PersonClass = class PersonClass2{
        constructor(name){
            this.name = name
        }
        sayName(){
            console.log(this.name)
        }
    }
    console.log(typeof PersonClass)          // function
    console.log(typeof PersonClass2)        // undefined                

兩者除了命名表達式需在關鍵字class后添加一個標識符外,並無其他差別,但是注意typeof PersonClass2 輸出的是undefined,這是因為PersonClass2只存在於類定義中,而在類的外部,其實並不存在一個名為PersonClass2的綁定。下邊我們用一段沒有使用關鍵字class的等價聲明來了解這背后的原理:

    let PersonClass = (function () {
        const PersonClass2 = function (name) {
            if (typeof new.target === "undefined") {      //類與構造函數差異的第五條
                throw new Error("必須通過new關鍵字調用構造函數")
            }
            this.name = name
        }
        Object.defineProperty(PersonClass2.prototype, 'sayName', {
            value: function () {
                if (typeof new.target !== "undefined") {    //類與構造函數差異的第四條
                    throw new Error("不可使用new關鍵字調用該方法")
                }
                console.log(this.name)
            },
            enumerable: false,
            writable: true,
            configurable: true,
        })
        return PersonClass2
    }())

外部作用域中的PersonClasss是用let聲明的,而立即表達式中的PersonClass2是用const聲明的,這也說明了為什么可以在外部修改類名而內部卻不可修改,且PersonClass2只能在類內部使用。

類的特點

下邊簡單介紹一下類中的幾個小特性

  • 一級公民類
    在編程中,能被當做值使用的就成為一級公民,意味着它可以被當做參數傳給函數,可以作為函數返回值,能用來賦值給變量,JS的類也可以被當成值使用,就是一級公民類。
  • 訪問屬性
    自有屬性需要在類構造器中創建,而類還允許你在原型上定義訪問器屬性。為了創建一個getter ,要使用 get 關鍵字,並要與后方標識符之間留出空格;創建 setter 用相同方式,只是要換用 set 關鍵字。
  • 需計算成員名
    類方法與類訪問器屬性也都能使用需計算的名稱。語法相同於對象字面量中的需計算名稱:無須使用標識符,而是用方括號來包裹一個表達式。
  • 生成器方法
    可以使用 Symbol.iterator 來定義生成器方法,從而定義出類的默認迭代器。
  • 靜態成員
    ES6 類的靜態成員的創建,只要在方法與訪問器屬性的名稱前添加正式的 static 標注。你能在類中的任何方法與訪問器屬性上使用 static 關鍵字,唯一限制是不能將它用於 constructor 方法的定義。

繼承與派生類

首先我們來看下ES6中是怎么實現繼承的:

    class Rectangle(){
        constructor(length,width){
            this.length = length
            this.width = width
        }
        getArea(){
            return this.length * this.width
        }
    }

    class Square extends Rectangle {
        constructor(length){
            //等價於Rectangle.call(this,length,length)
            super(length,length)
        }
    }
    var square = new Square(3)
    console.log(square.getArea())              //9
    console.log(square instanceof Suqare)     //true
    console.log(square instanceof Rectangle) //true

ES5中Square繼承自Rectangle,為了這樣做,必須創建自Rectangle.prototype的新對象重寫Square.prototype並調用Rectangle。call()方法,這些步驟很容易讓人感到困惑,並在這里犯錯。ES6類的出現讓我們輕松實現了繼承功能,使用extends關鍵字可以指定類繼承的函數,繼承自其他類的類被稱作派生類,如果在派生類中指定了構造函數則必須要調用super(),通過調用super()方法即可訪問基類的構造函數,原型會自動調整,如果不這樣做,程序就會報錯。如果選擇不適用構造函數,則當創建新的類實例時會自動調用super()並傳入所有參數。舉個例子,一下兩個類完全相同:

    class Square extends Rectangle{
        //沒有構造函數
    }

    clas Square extends Rectangle{
        constructor(...args){
            super(...args)
        }
    }

派生類的特性

派生類除了上述的特征外,還有一些其他的特性:

  1. 派生類中的方法總會覆蓋基類中的同名方法。
  2. 如果基類中有靜態成員,那么這些靜態成員在派生類中也可用。
  3. 只要表達式可以被解析為一個函數並且具有[[constructor]]屬性和原型,就可以用extends進行派生。
  4. ES6支持內建對象的繼承。

ES6 的類讓 JS 中的繼承變得更簡單,是javaScript新特性的一個重要組成部分,這一特性提供了一種更簡潔的語法和更好的功能,可以讓你通過一個安全、一致的方法來自定義對象類型。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM