深入解析 JavaScript 的 Error.isError()
Error.isError() 可跨 realm 判断真实的 JavaScript 错误,解释它为何优于 instanceof Error,并介绍安全回退用法。
Error.isError(value) 是一个静态方法,仅当 value 是真正的 Error 对象时才返回 true。由于它检查的是内部标记([[ErrorData]])而非遍历原型链,因此即使跨 realm(领域)也能保持可靠。
如果你曾经打开错误追踪工具,却在本该出现真实异常的地方看到一个空的 {},那你已经遇到过这个方法所要解决的问题了。在某个环节中,某次 instanceof 检查悄无声息地判定你的错误不是错误。该方法已在 ECMAScript 2026 中标准化,这意味着 instanceof Error 长期存在的缺陷(来自 iframe 的错误被判定为非错误,而伪造对象却被判定为错误)终于有了一流的解决方案。本文将讲解该方法的作用、它为何优于 instanceof、其背后的确切机制、边界情况,以及如何配合安全回退方案来采用它。
其行为完全符合预期:Error.isError(new Error()) 为 true,而 Error.isError({ message: 'x' }) 为 false,因为该方法验证的是对象是如何被构造的,而不仅仅是它继承自什么。
核心要点
Error.isError()针对内部[[ErrorData]]槽执行带标记(branded)检查,与Array.isArray()所使用的不可伪造检查属于同一类别,因此用户态代码无法蒙混过关。instanceof Error会以两种相反的方式失效:对于在另一个 realm 中创建的真实错误返回false,而对于原型被设置为Error.prototype的伪造对象返回true。- 该方法对
TypeError等内置子类、正确extend Error的类,以及浏览器中的DOMException均返回true,不过 Safari 目前对DOMException返回false。 Error.isError()是 ECMAScript 2026 的一部分,已在 Chrome/Edge 134+、Firefox 138+、Node.js 24.0.0+ 以及 Safari 18.4(部分支持)中发布。- 请在边界处使用它(全局处理器、日志衔接代码、Worker、iframe、SSR/边缘计算),在这些场景中,
instanceof的一次静默漏判会让真实错误在日志里变成一个空对象。
为什么 instanceof Error 不够用?
Discover how at OpenReplay.com.
instanceof Error 会以两种相反的方式失效,且两者都是静默发生的。TC39 提案阐明了第一种情况:一个跨越了 realm 边界的真实错误(无论来自 iframe 还是 Node 的 vm 模块)会返回假阴性结果。每个 realm 都有自己的 Error 构造函数,因此在 iframe 中创建的错误并不是你的 Error 的实例。
第二种失效恰好相反:任何原型链中包含 Error.prototype 的对象都能通过检查,而它并非真正的错误。以下代码展示了这两种失效:
// Failure 1 — cross-realm error reads as NOT an error
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
const crossRealmError = new iframe.contentWindow.Error('from iframe');
crossRealmError instanceof Error; // → false (wrong)
Error.isError(crossRealmError); // → true (correct)
// Failure 2 — fake object reads as an error
const fake = { message: "I'm not real" };
Object.setPrototypeOf(fake, Error.prototype);
fake instanceof Error; // → true (wrong)
Error.isError(fake); // → false (correct)
这两个结果都是有文档规定的契约,而非偶然。MDN 对该方法的参考文档将其定位为 instanceof Error 的稳健替代方案,原因正是它规避了这两种失效模式:借用原型不足以通过检查,而在另一个 realm 中构建的错误仍能通过检查。instanceof 沿着原型链比较构造函数身份,因此在这两种情况下都会给出错误结论。
| 输入 | instanceof Error | 鸭子类型('message' in x) | Error.isError() |
|---|---|---|---|
跨 realm 的 Error(iframe/worker/vm) | ❌ false | ⚠️ 视情况而定 | ✅ true |
Object.setPrototypeOf(obj, Error.prototype) | ❌ true | ⚠️ true | ✅ false |
class MyError extends Error 的实例 | ✅ true | ✅ true | ✅ true |
Error.isError() 的底层原理是什么?
在底层,Error.isError() 针对内部槽执行带标记检查,而不是检视原型链。MDN 直接描述了这一机制:该方法查找的是 Error() 构造函数在其所构建的每个错误上安装的私有字段。这与 Array.isArray() 背后的手法完全相同,也与 in 运算符测试属性的方式颇为相近。
Array.isArray() 这个类比正是应当牢记的思维模型。Array.isArray() 同样接受在不同 realm 中构建的数组,而在这种情况下 instanceof Array 会返回 false,因为每个 realm 持有各自独立的 Array 构造函数。Error.isError() 为错误对象带来了同样的、对 realm 安全的标记机制。
Stage 4 规范文本将该槽命名为 [[ErrorData]],并将 IsError 操作保持为三个步骤:任何非对象立即判定失败,任何带有该槽的值判定通过,其余一律判定失败。该槽在构造时设置,无法从 JavaScript 中伪造。
为什么用内部槽而不是 Object.prototype.toString?因为标签伪造已经攻破了这个老办法。提案作者将这个问题提交给了委员会:自从 Symbol.toStringTag 出现后,一项本来既可靠又无法伪造的检查,两方面都不再成立。而且由于除 Object#toString 之外没有任何东西会查询错误槽,用户代码根本无从获得可靠的检测手段。Error.isError() 恰好填补了这一空白。
值得了解的行为细节
Error.isError() 对整个 Error 家族返回 true,对其他一切返回 false,且不会抛出异常。MDN 的示例显示 new Error()、new TypeError() 和 new DOMException() 都返回 true,而不带参数调用,或传入 {}、null、undefined、17 或字符串 "Error" 时则返回 false。由于规范中的谓词对任何非对象以及缺少该槽的对象都返回 false,因此原始值和 null 都会被干净地处理,而不会引发异常。
正确扩展的自定义类能够被识别,因为它们继承了该标记:
class ValidationError extends Error {}
Error.isError(new ValidationError('bad input')); // → true
只有那些从未调用 Error 构造函数的仿冒对象才会被拒绝。DOMException 的情况有一个值得记住的细微之处。MDN 的规则是 DOMException 实例可以通过检查。DOMException 在形式上并不是 Error 的子类,因为其构造函数并不继承自 Error 构造函数,但它携带相同的标记,所以带标记的检查仍将其视为错误。Safari 是个例外:Chrome 在 Firefox 138 发布当月的综述中记录了 Safari 对 DOMException 返回 false,这也是为什么尽管所有主流引擎现已实现该方法,它仍未达到 Baseline 状态。出于同样的原因,MDN 仍将其标记为有限可用(limited availability)。请将这一个案例视为尚未统一的情况。
何时使用 Error.isError()
请在边界处使用 Error.isError()(全局错误处理器、日志与错误上报的衔接代码、测试运行器、库、SSR/边缘计算、Worker、iframe 以及浏览器扩展),在这些位置,instanceof 的一次静默漏判会让真实错误在日志里变成一个空的 {}。在作用域收窄、同一 realm 内的代码中,普通的 instanceof 完全够用;真正的收益体现在值跨越执行上下文的边缘地带。
这对应着一种真实的上报失效模式:边界处的 instanceof 检查将一个真正抛出的错误重新归类为普通对象,于是它进入你的数据管道时既没有消息也没有堆栈。会话回放(Session replay)在此是一项有用的技术:回放会话可以呈现出实际抛出的控制台错误,从而暴露浏览器所见与你的衔接代码所上报内容之间的差距。解决办法是在这些边界处、在任何内容被序列化或记录之前,使用 Error.isError() 进行标记检查。
浏览器与运行时支持,以及安全回退方案
Error.isError() 是 ECMAScript 2026(第 17 版)的一部分,Ecma International 于 2026 年 6 月 30 日批准了该版本;该提案在 2025 年 5 月的 TC39 会议上进入 Stage 4。在浏览器中,它从 Chrome 和 Edge 134、Safari 18.4 以及 2025 年 4 月 29 日发布的 Firefox 138 开始可用。在服务端,Node.js 24.0.0 通过升级到 V8 13.6 引入了该特性,同期落地的还有 Float16Array、显式资源管理、RegExp.escape 以及 WebAssembly Memory64。
若想实现可直接替换、并在旧环境上优雅降级的方案,可进行特性检测:
function isError(value) {
return typeof Error.isError === 'function'
? Error.isError(value) // realm-safe on modern engines
: value instanceof Error; // fallback, not realm-safe
}
在 TypeScript 中,Error.isError(e) 同时充当类型守卫(type guard),可在 if 分支内将捕获到的 unknown 值收窄为 Error,因此无需手动断言即可类型安全地访问 e.message。
结论
Error.isError() 弥补了鸭子类型和 instanceof 永远无法填补的空白:它询问的是引擎是否真的将某个值标记为错误,因此跨 realm 的错误和原型伪造的仿冒对象都能得到正确的判定。今天就把你的边界检查(日志衔接代码、全局处理器、Worker 与 iframe 的接缝处)切换到带特性检测的封装函数上,只在代码从不离开自身 realm 的地方保留 instanceof。
常见问题
Error.isError() 是已经标准化,还是仍处于实验性提案阶段?
Error.isError() 已完全标准化。它在 2025 年 5 月的第 108 次会议上推进到了 TC39 流程的 Stage 4,并被纳入 ECMAScript 2026,即该语言规范的第 17 版。它不再是提案或实验性特性,因此那些称其“尚未标准化”或处于“Stage 3”的说法已经过时。请将其视为已正式发布的语言特性。
Error.isError() 对自定义错误类有效吗?
有效,前提是该类正确地扩展了 Error。定义为 'class MyError extends Error {}' 的类会继承 Error 构造函数设置的内部标记,因此 Error.isError(new MyError()) 返回 true。只有那些从未调用 Error 构造函数的仿冒对象会被拒绝,例如一个被强行将 Error.prototype 塞入原型链的普通对象。关键要求是正确的子类化,而不是类的名称。
Error.isError() 在 Safari 中可用吗?
Safari 18.4 及更高版本对常规 Error 对象支持 Error.isError(),但支持是部分性的。Safari 目前对 DOMException 实例返回 false,而规范和其他引擎返回 true。由于这一差距,MDN 未将该方法归类为 Baseline,web.dev 也标注其尚未实现统一可用。如果你的代码面向 Safari,请对 DOMException 的情况做防御性处理。
Error.isError() 比 instanceof Error 更快吗?
两者实际上都是常数时间的检查,因此性能并不是切换的理由。instanceof 会遍历原型链,而 Error.isError() 只读取单个内部标记,但实际差异可以忽略不计。真正的优势在于正确性:对于跨 realm 的错误和原型伪造的仿冒对象,Error.isError() 会给出正确答案,而 instanceof 在这些情况下会静默失效。选择它是为了在执行上下文边界上获得可靠性,而不是为了速度。
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Star on GitHub12k