與函數類型相似,定義類也有兩種主要方式:類聲明和類表達式,這兩種方式都使用class關鍵字加大括號
與函數表達式類似,類表達式在它們被求值前也不能引用,不過與函數定義不同的是,雖然函數聲明可以提升,但類定義不能,函數受函數作用域限制,而類受塊作用域限制
類可以包含構造函數方法、實例方法、獲取函數、 設置函數和靜態類方法,但這些都不是必須的,空的類定義照樣有效,默認情況下,類定義中的代碼都在嚴格模式下執行
類表達式的名稱是可選的,在把類表達式賦值給變量后,可以通過name屬性取得類表達式的名稱字符串,但不能在類表達式作用域外部訪問這個標識符
constructor關鍵字用于在類定義塊內部創建類的構造函數,方法名constructor會告訴解釋器在使用new操作符創建類的新實例時,應該調用這個函數。構造函數的定義不是必須的,不定義構造函數相當于將構造函數定義為空函數
使用new操作符實例化操作等于使用new調用其構造函數,唯一可感知的不同之處是,JS解釋器知道使用new和類意味著應該使用constructor函數進行實例化。
使用new調用類的構造函數會執行如下操作:
- 在內存中創建一個新對象
- 這個新對象內部的[[Prototype]]指針被賦值為構造函數的prototype屬性
- 構造函數內部的this被賦值為這個新對象
- 執行構造函數內部的代碼
- 如果構造函數返回非空對象,則返回該對象,否則,返回剛創建的新對象
類實例化時傳入的參數會用作構造函數的參數,如果不需要參數,則類名后面的括號也是可選的
默認情況下,類構造函數會在執行之后返回this對象,構造函數返回的對象會被用作實例化的對象,如果沒有什么引用新創建的this對象,那么這個對象會被銷毀。如果返回的不是this對象,而是其他對象,那么這個對象不會通過instanceof操作符檢測出跟類有關聯,因為這個對象的原型指針并沒有被修改
類構造函數與構造函數的主要區別是,調用類構造函數必須使用new操作符,而普通構造函數如果不使用new調用,那么就會以全局的this作為內部對象,調用類構造函數時如果忘了使用new則會拋出錯誤
類構造函數沒有什么特殊之處,實例化之后,他會成為普通的實例方法,因此,實例化之后可以在實例上引用它,但作為類構造函數,依然要使用new調用
ES類就是一種特殊函數,類標識符有prototype屬性,而這個原型也有一個constructor屬性指向類自身
與普通構造函數一樣,可以使用instanceof操作符檢查構造函數原型是否存在于實例的原型鏈中
類本身具有與普通構造函數一樣的行為,在類的上下文中,類本身在使用new調用時就會被當成構造函數,類中定義的constructor方法不會被當成構造函數,在對它使用instanceof操作符時會返回false,但是,如果在創建實例時直接將類構造函數當成普通構造函數來使用,那么instanceof操作符的返回值會反轉
類是JS一等公民,因此可以像其他對象或函數引用一樣把類作為參數傳遞
與立即調用函數表達式類似,類也可以立即實例化
通過new調用類標識符時,都會執行類構造函數,在這個函數內部,可以為新創建的實例添加自有屬性,至于添加什么樣的屬性,則沒有限制。另外,在構造函數執行完畢后,仍然可以給實例繼續添加新成員
每個實例都對應一個唯一的成員對象,這意味著所有成員都不會在原型上共享
為了在實例間共享方法,類定義語法把在類塊中定義的方法作為原型方法。可以把方法定義在類構造函數中或者類塊中,但不能在類塊中給原型添加原始值或對象作為成員數據
類方法等同于對象屬性,因此可以使用字符串、符號或計算的值作為鍵,類定義也支持獲取和設置訪問器,語法與行為跟普通對象一樣
可以在類上定義靜態方法,這些方法通常用于執行不特定于實例的操作,也不要求存在類的實例。與原型成員類似,靜態成員每個類上只能有一個,靜態類方法非常適合作為實例工廠
靜態類成員在類定義中使用static關鍵字作為前綴,在靜態成員中,this引用類自身,其他所有約定跟原型成員一樣
雖然類定義并不顯式支持在原型或類上添加成員數據,但在類定義外部,可以手動添加
類定義語法支持在原型和類本身上定義生成器方法,因為支持生成器方法,所以可以通過添加一個默認的迭代器,把類實例變成可迭代對象
ES6類支持單繼承,使用extends關鍵字,就可以繼承任何擁有[[Constructor]]和原型的對象,很大程度上,這意味著不僅可以繼承一個類,也可以繼承普通的構造函數。派生類都會通過原型鏈訪問到類和原型上定義的方法,this的值會反映調用相應方法的實例或者類
派生類的方法可以通過super關鍵字引用它們的原型,這個關鍵字只能在派生類中使用,而且僅限于類構造函數、實例方法和靜態方法內部。在類構造函數中使用super可以調用父類構造函數
在靜態方法中可以通過super調用繼承的類上定義的靜態方法
ES6給類構造函數和靜態方法添加了內部特性[[HomeObject]],這個特性是一個指針,指向定義該方法的對象,這個指針是自動賦值的,而且只能在JS引擎內部訪問。super始終會定義為[[HomeObject]]的原型
在使用super時要注意幾個問題:
- super只能在派生類構造函數和靜態方法中使用
- 不能單獨引用super關鍵字,要么用它調用構造函數,要么用它引用靜態方法
- 調用super會調用父類構造函數,并將返回的實例賦值給this
- super的行為如同調用構造函數,如果需要給父類構造函數傳參,則需要手動傳入
- 如果沒有定義類構造函數,在實例化派生類時會調用super,而且會傳入所有傳給派生類的參數
- 在類構造函數中,不能在調用super之前引用this
- 如果在派生類中顯示定義了構造函數,則要么必須在其中調用super,要么必須在其中返回一個對象
有時候可能需要定義這樣一個類,它可供其他類繼承,但本身不會被實例化。可以通過new.target實現,new.target保存通過new關鍵字調用的類或函數,通過在實例化時檢測new.target是不是抽象基類,可以阻止對抽象基類的實例化
通過在抽象基類構造函數中進行檢查,可以要求派生類必須定義某個方法,因為原型方法在調用類構造函數之前就已經存在了,所以可以通過this關鍵字來檢查相應的方法
ES6類為繼承內置引用類型提供了順暢的機制,開發者可以方便的擴展內置類型
有些內置類型的方法,會返回新實例,默認情況下,返回實例的類型與原始實例的類型是一致的,如果想覆蓋這個默認行為,則可以覆蓋Symbol.species訪問器,這個訪問器決定在創建返回的實例時使用的類
把不同類的行為集中到一個類是一種常見的JS模式,extends關鍵字后面是一個JS表達式,任何可以解析為一個類或一個構造函數的表達式都是有效的,這個表達式會在求值類定義時被求值。混入模式可以通過在一個表達式中連綴多個混入元素來實現,這個表達式最終會解析為一個可以被繼承的類。可以定義一組可嵌套的函數,每個函數分別接收一個超類作為參數,而將混入類定義為這個參數的子類,并返回這個類,這些組合函數連綴調用,最終組合成超類表達式