內建的類,例如 Array
,Map
等也都是可以擴展的(extendable)。
例如,這裏有壹個繼承自原生 Array
的類 PowerArray
:
// 給 PowerArray 新增了壹個方法(可以增加更多) 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
等方法中創建新實體的 constructor
。
如果我們希望像 map
或 filter
這樣的內建方法返回常規數組,我們可以在 Symbol.species
中返回 Array
,就像這樣:
class PowerArray extends Array { isEmpty() { return this.length === 0; } // 內建方法將使用這個作爲 constructor static get [Symbol.species]() { return Array; } } let arr = new PowerArray(1, 2, 5, 10, 50); alert(arr.isEmpty()); // false // filter 使用 arr.constructor[Symbol.species] 作爲 constructor 創建新數組 let filteredArr = arr.filter(item => item >= 10); // filteredArr 不是 PowerArray,而是 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
獲得的繼承相比,這是內建對象之間繼承的壹個重要區別。