2021 年 12 月 12 日

擴充內建類別

內建類別,例如 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

請注意一件非常有趣的事。內建方法,例如 filtermap 等,會傳回繼承類別 PowerArray 的新物件。它們的內部實作會使用物件的 constructor 屬性來達成此目的。

在上面的範例中,

arr.constructor === PowerArray

當呼叫 arr.filter() 時,它會在內部使用 arr.constructor 建立新的結果陣列,而不是基本的 Array。這其實很酷,因為我們可以在結果上繼續使用 PowerArray 方法。

更棒的是,我們可以自訂此行為。

我們可以新增一個特殊的靜態 getter Symbol.species 到類別中。如果它存在,它應該傳回 JavaScript 會在內部使用來建立 mapfilter 等新實體的建構函式。

如果我們希望內建方法(例如 mapfilter)回傳一般陣列,我們可以在 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。因此,擴充功能不會再進一步傳遞。

其他集合運作方式類似

其他集合,例如 MapSet,運作方式類似。它們也使用 Symbol.species

內建函式中沒有靜態繼承

內建物件有自己的靜態方法,例如 Object.keysArray.isArray 等。

我們已經知道,原生類別會互相擴充。例如,Array 會擴充 Object

通常,當一個類別擴充另一個類別時,靜態和非靜態方法都會被繼承。這在文章 靜態屬性和方法 中有詳細說明。

但內建類別是個例外。它們不會互相繼承靜態函式。

例如,ArrayDate 都繼承自 Object,因此它們的執行個體有來自 Object.prototype 的方法。但 Array.[[Prototype]] 沒有參照 Object,因此沒有 Array.keys()(或 Date.keys())等靜態方法。

以下是 DateObject 的圖片結構

如你所見,DateObject 之間沒有連結。它們是獨立的,只有 Date.prototype 會繼承自 Object.prototype

這是內建物件與我們使用 extends 所獲得的繼承之間的重要差異。

教學課程地圖

留言

留言前請先閱讀…
  • 如果您有改善建議 - 請 提交 GitHub 問題 或提交拉取請求,而不是發表評論。
  • 如果您無法理解文章中的內容 – 請詳細說明。
  • 若要插入幾行程式碼,請使用 <code> 標籤,若要插入多行程式碼,請將它們包覆在 <pre> 標籤中,若要插入超過 10 行程式碼,請使用沙盒 (plnkrjsbincodepen…)