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…)