symbol.species 决定内置方法返回对象类型,需在子类中定义 static get [symbol.species]() { return this; } 以保持类型一致;影响 map、filter、then 等方法,但 array.from、push 等不遵循该协议。

Symbol.species 直接决定子类调用内置方法后返回的对象类型,而不是继承关系本身。它不改变 instanceof 的原型链判断逻辑,但会干预 map、filter、slice、then 等方法内部“用谁来 new 一个新实例”——这个选择错了,子类就“断链”了。
它解决的核心问题:方法返回的不是你的子类
比如你写了一个继承 Array 的类:
- 没设 Symbol.species →
myArray.map(...)返回的是普通Array实例,不是你的子类 -
result instanceof MyArray是false,所有自定义方法(如sum()、log())都不可用 - 原因:这些方法内部不直接调用
this.constructor,而是读取this.constructor[Symbol.species] - 默认该值是
undefined,引擎自动 fallback 到原生构造器(如Array)
怎么正确设置才能保持类型一致
必须在子类中定义静态 getter:
- 写法固定:
static get [Symbol.species]() { return this; } - 用
this而不是硬编码类名,支持多层继承(如SuperArray extends MyArray) - 返回值必须是可构造函数(有
new能力),否则运行时报错 - 返回
Array或其他兼容构造器也可以,但意味着主动放弃子类行为
哪些方法受它控制,哪些完全无视
走 Symbol.species 协议的方法:
- Array:map、filter、slice、concat、flat、flatMap、toReversed、with、splice(有返回时)
- Promise:then、catch(返回新 Promise 实例)
- TypedArray、RegExp(部分引擎)、Map/Set(规范支持,实际实现略有差异)
完全不查 Symbol.species 的操作:
- Array.from()、Array.of() —— 只认显式传入的 constructor 或默认 Array
- push/pop/shift/unshift —— 就地修改,不创建新实例
- 扩展运算符
[...arr]、JSON.parse()、手动new MyArray() - Lodash 等第三方库的
_.map()—— 它们不调用原生方法,也不读 Symbol.species
Promise 子类更依赖它,稍错就失效
Promise 的链式调用对 Symbol.species 更敏感:
-
promise.then(...).then(...)中第二个then的上下文是前一个then返回的新 Promise - 如果
[Symbol.species]返回Promise,后续链就退化为原生 Promise,自定义拦截、超时、日志全失效 - 必须返回
this,且确保子类 constructor 兼容标准签名(比如接受(executor),不能强制加额外参数)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











