需设置 Symbol.species 静态访问器为 return this,因内置集合方法(如 map、filter)内部通过 this.constructor[Symbol.species] 创建新实例;若未定义,派生类调用时将返回原生类型而非子类实例。

要让派生自 Set、Map、Array 等内置集合类的子类,在调用 map(或其他返回新实例的方法,如 filter、slice)时生成的是**当前子类实例**而非原始内置类,关键在于正确设置静态访问器属性 Symbol.species。
为什么需要 Symbol.species?
默认情况下,内置可迭代集合类(如 Array.prototype.map)在内部创建新实例时,并不直接使用 this.constructor,而是读取 this.constructor[Symbol.species]。如果未显式定义,该值默认为 this.constructor(对原生类即自身),但对派生类而言,若父类(如 Array)的 [Symbol.species] 指向自身(Array),那么即使你在子类中重写了方法,map 内部仍会 new Array,导致返回原生类型。
如何正确定义 Symbol.species
在子类中,将 Symbol.species 静态访问器设为 return this(即构造函数自身),确保所有继承自内置类的衍生方法都使用当前子类构造新实例:
<!-- 示例:自定义 MyArray 继承 Array -->
class MyArray extends Array {
static get [Symbol.species]() {
return this;
}
}
<p>const arr = new MyArray(1, 2, 3);
const mapped = arr.map(x => x * 2);</p><p>console.log(mapped instanceof MyArray); // true
console.log(mapped instanceof Array); // true(因为 MyArray 是 Array 的子类)
console.log(mapped.constructor === MyArray); // true
对 Set/Map 派生类同样适用
Set.prototype.map 并不存在 —— 注意:原生 Set 和 Map 实例本身没有 map 方法。但它们的子类若自行实现或通过扩展添加了类似逻辑(例如封装一个 mapValues),或使用第三方库(如 immutable.js 风格 API),只要内部调用了 new this.constructor[Symbol.species](...),就需设置 Symbol.species。
更常见的是:你扩展了 Set,并希望 filter、union 等自定义方法返回本类实例。此时只要这些方法内部按规范使用 constructor[Symbol.species],设置方式一致:
- 在子类中声明
static get [Symbol.species]() { return this; } - 确保所有创建新集合的内部逻辑都通过
this.constructor[Symbol.species]获取构造器 - 避免硬编码
new MySet(...),改用new (this.constructor[Symbol.species])(...)
注意事项与常见陷阱
-
Symbol.species只影响“由方法自动创建的新实例”,不影响手动new Subclass()或显式调用构造器 - 不能用普通静态属性赋值(
Subclass[Symbol.species] = Subclass),必须是get访问器,否则某些引擎可能忽略 - 若子类想强制返回父类(如总是返回
Array而非MyArray),可返回Array;但通常目的就是保持子类身份,所以返回this最自然 -
ArrayBuffer、TypedArray等也支持Symbol.species,规则相同

















