Skip to content

扩展内建类

扩展内建类指的是:让自定义类继承 ArrayErrorMap 这类 JavaScript 已有的内建构造器。

什么场景会用到

更常见的情况包括:

  • 想给数组补充一组领域方法
  • 想定义自定义错误类型
  • 想在保留原有行为的基础上增加额外能力

基本写法

js
class PowerArray extends Array {
  isEmpty() {
    return this.length === 0
  }
}

const arr = new PowerArray(1, 2, 3)

console.log(arr.isEmpty()) // false
console.log(arr instanceof Array) // true
console.log(arr instanceof PowerArray) // true

这里 PowerArray 既保留了数组行为,又增加了自己的方法。

扩展 Array

这是最常见的例子:

js
class PowerArray extends Array {
  last() {
    return this[this.length - 1]
  }
}

const arr = new PowerArray(1, 2, 3)
console.log(arr.last()) // 3

这样得到的实例仍然可以继续使用 pushmapfilter 等数组方法。

内建方法返回的结果类型

扩展内建类时,一个容易忽略的问题是:某些内建方法返回的新对象,默认也会沿用子类类型。

js
class PowerArray extends Array {}

const arr = new PowerArray(1, 2, 3)
const filtered = arr.filter((item) => item > 1)

console.log(filtered instanceof PowerArray) // true

这通常是合理的,但有时并不是想要的结果。

Symbol.species

如果希望这类派生操作返回原始内建类型,而不是子类,可以使用 Symbol.species

js
class PowerArray extends Array {
  static get [Symbol.species]() {
    return Array
  }
}

const arr = new PowerArray(1, 2, 3)
const filtered = arr.filter((item) => item > 1)

console.log(filtered instanceof PowerArray) // false
console.log(filtered instanceof Array) // true

可以把它理解成“派生结果默认应该是什么构造器”。

扩展 Error

另一个很常见的场景是自定义错误类型:

js
class ValidationError extends Error {
  constructor(message) {
    super(message)
    this.name = 'ValidationError'
  }
}

throw new ValidationError('invalid email')

这样可以:

  • 保留原有错误对象结构
  • instanceof 区分错误类型
  • 在业务里按类型处理错误

扩展内建类不是“随便加方法”

虽然语法允许继承内建类,但是否真的适合,要看类型本身和团队约定。

例如:

  • Array 增加少量强语义方法,通常还算自然
  • MapSet 做强领域封装,也可能合理
  • 把大量业务逻辑塞进 Array 子类,往往会让边界变糊

和直接改原型不是一回事

扩展内建类:

js
class PowerArray extends Array {}

修改全局原型:

js
Array.prototype.last = function () {
  return this[this.length - 1]
}

两者差别很大。前者只影响自己的子类实例,后者会污染所有数组实例。

一般更应避免直接改内建原型,尤其是在应用代码和共享库里。

什么时候适合用

更适合:

  • 需要保留内建类型行为
  • 只增加少量明确能力
  • 需要 instanceof 和类型层级

如果只是想复用几个辅助方法,工具函数通常更直接。

使用建议

  • 只有在“它本质上仍然是那个内建类型”时,再考虑继承内建类。
  • 自定义错误类型是扩展内建类里最实用的一类场景。
  • 遇到返回结果类型不符合预期时,优先想到 Symbol.species
  • 不要用“继承内建类”替代普通工具函数或组合设计。

基于 MIT 许可发布