內建類別,例如 Array、Map 等,也可以擴充。
例如,這裡的 PowerArray
繼承自原生 Array
// add one more method to it (can do more)
class PowerArray extends Array {
isEmpty() {
return this.length === 0;
}
}
let arr = new PowerArray(1, 2, 5, 10, 50);
alert(arr.isEmpty()); // false
let filteredArr = arr.filter(item => item >= 10);
alert(filteredArr); // 10, 50
alert(filteredArr.isEmpty()); // false
請注意一件非常有趣的事。內建方法,例如 filter
、map
等,會傳回繼承類別 PowerArray
的新物件。它們的內部實作會使用物件的 constructor
屬性來達成此目的。
在上面的範例中,
arr.constructor === PowerArray
當呼叫 arr.filter()
時,它會在內部使用 arr.constructor
建立新的結果陣列,而不是基本的 Array
。這其實很酷,因為我們可以在結果上繼續使用 PowerArray
方法。
更棒的是,我們可以自訂此行為。
我們可以新增一個特殊的靜態 getter Symbol.species
到類別中。如果它存在,它應該傳回 JavaScript 會在內部使用來建立 map
、filter
等新實體的建構函式。
如果我們希望內建方法(例如 map
或 filter
)回傳一般陣列,我們可以在 Symbol.species
中回傳 Array
,如下所示
class PowerArray extends Array {
isEmpty() {
return this.length === 0;
}
// built-in methods will use this as the constructor
static get [Symbol.species]() {
return Array;
}
}
let arr = new PowerArray(1, 2, 5, 10, 50);
alert(arr.isEmpty()); // false
// filter creates new array using arr.constructor[Symbol.species] as constructor
let filteredArr = arr.filter(item => item >= 10);
// filteredArr is not PowerArray, but Array
alert(filteredArr.isEmpty()); // Error: filteredArr.isEmpty is not a function
如你所見,現在 .filter
會回傳 Array
。因此,擴充功能不會再進一步傳遞。
其他集合,例如 Map
和 Set
,運作方式類似。它們也使用 Symbol.species
。
內建函式中沒有靜態繼承
內建物件有自己的靜態方法,例如 Object.keys
、Array.isArray
等。
我們已經知道,原生類別會互相擴充。例如,Array
會擴充 Object
。
通常,當一個類別擴充另一個類別時,靜態和非靜態方法都會被繼承。這在文章 靜態屬性和方法 中有詳細說明。
但內建類別是個例外。它們不會互相繼承靜態函式。
例如,Array
和 Date
都繼承自 Object
,因此它們的執行個體有來自 Object.prototype
的方法。但 Array.[[Prototype]]
沒有參照 Object
,因此沒有 Array.keys()
(或 Date.keys()
)等靜態方法。
以下是 Date
和 Object
的圖片結構
如你所見,Date
和 Object
之間沒有連結。它們是獨立的,只有 Date.prototype
會繼承自 Object.prototype
。
這是內建物件與我們使用 extends
所獲得的繼承之間的重要差異。
留言
<code>
標籤,若要插入多行程式碼,請將它們包覆在<pre>
標籤中,若要插入超過 10 行程式碼,請使用沙盒 (plnkr、jsbin、codepen…)