javascript深拷贝error对象需专用方法,因name、message、stack等关键属性不可枚举或为getter,json.stringify等通用方式会丢失信息;应通过object.getownpropertynames获取自有属性,用原构造函数新建实例,并递归处理cause字段。

JavaScript 中深拷贝 Error 对象不能靠通用方法(如 JSON.parse(JSON.stringify()) 或简单递归),因为它的关键属性(name、message、stack)大多不可枚举或为 getter,直接拷贝会丢失信息甚至变成空对象。
为什么常规深拷贝会失败
Error 实例看似普通对象,但内部结构特殊:
-
message和name是自有属性,通常可读,但部分环境(如某些打包配置)下name可能被冻结或不可枚举 -
stack几乎总是 accessor 属性(getter),JSON.stringify()完全忽略它,for...in或Object.keys()也遍历不到 - 子类(如
SyntaxError、TypeError)的构造器需保留,不能统一用Error新建 - 现代扩展字段(如
cause、code、errno)属于自有属性,但容易被遗漏
手动重建是最可靠方式
核心思路:读取源错误的关键字段,用原构造函数新建实例,并复制所有自有属性。
- 用
err.constructor判断类型,优先复用原构造器(new err.constructor(message)),确保子类行为一致 - 用
Object.getOwnPropertyNames(err)获取全部自有属性名(含code、cause等),避开不可枚举陷阱 - 对
cause字段递归调用深拷贝函数(若存在且为 Error 实例),避免浅引用 - 安全读取
stack:用Object.getOwnPropertyDescriptor(err, 'stack')?.get?.call(err),防止因权限或 strict mode 报错
兼容 structuredClone 的边界情况
structuredClone() 在较新环境(Chrome 98+、Firefox 94+、Safari 15.4+)已支持 Error 克隆,但仍有局限:
- 能保留
name、message、stack和常见扩展字段(如code) - 不保证
cause被递归克隆(部分浏览器仍浅拷贝) - 无法处理自定义 getter 或 Symbol 属性(Error 本身不用 Symbol,但业务封装类可能添加)
- 若需跨旧环境或强一致性控制,仍建议封装专用
cloneError()工具函数
一个轻量实用的克隆函数
可嵌入现有深拷贝流程中,专用于 Error 类型:
function cloneError(err) {
if (!(err instanceof Error)) return err;
const Cls = err.constructor === Error ? Error : err.constructor;
const props = Object.getOwnPropertyNames(err);
const copy = new Cls(err.message);
// 复制所有自有属性(包括 cause、code、errno 等)
for (const key of props) {
if (key !== 'message') {
copy[key] = key === 'cause' && err[key] instanceof Error
? cloneError(err[key]) // 递归克隆 cause
: err[key];
}
}
// 尝试提取 stack(安全兜底)
const stackDesc = Object.getOwnPropertyDescriptor(err, 'stack');
if (stackDesc && typeof stackDesc.get === 'function') {
try {
copy.stack = stackDesc.get.call(err);
} catch (e) {
// 忽略 stack 读取失败,不影响主体
}
}
return copy;
}Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











