对象拷贝导致类型丢失的根本原因是默认机制无法还原特殊内置类型的构造逻辑,修复关键在于用构造函数显式重建:error、date、regexp、map/set、typedarray等因含不可枚举属性或私有槽位而易丢失,需按类型单独调用构造函数并填充状态,json/object.assign因仅处理可枚举属性而失效。

对象拷贝导致的类型丢失,根本原因不是“复制不全”,而是默认拷贝机制(比如浅拷贝、JSON序列化、简单递归)无法识别并还原特殊内置类型的构造逻辑。修复的关键不是在拷贝后修补,而是在构造新对象时就按原类型语义重建——也就是用构造函数显式初始化。
哪些类型容易丢失?
常见易丢类型的共性是:它们有内部不可枚举属性、私有槽位([[PrimitiveValue]])、或依赖特定构造器行为。例如:
-
Error:丢失
name、stack、cause,仅剩空对象 -
Date:
toString()正常但getTime()返回 NaN,因内部时间值未还原 -
RegExp:正则表达式变成普通对象,
test()报错 - Map / Set:变成空集合,键值对全部消失
-
TypedArray:
buffer引用丢失,length错误或为 0
用构造函数修复的核心逻辑
不是“深拷贝后再修正”,而是“识别类型 → 调用对应构造函数 → 填充关键状态”。每类需单独处理:
-
Error:用
new obj.constructor(obj.message)创建实例,再手动赋name、stack、cause -
Date:直接
new Date(obj.getTime()),无需遍历属性 -
RegExp:
new RegExp(obj)即可完整还原(含 flags) -
Map / Set:解构后重建 ——
new Map([...obj.entries()].map(([k, v]) => [clone(k), clone(v)])) -
TypedArray:先
new obj.constructor(obj),再根据需要决定是否obj.buffer.slice()隔离底层 buffer
为什么不能靠 JSON 或 Object.assign?
因为它们只处理可枚举自有属性,且强制转为纯数据:
-
JSON.stringify(new Date())→ 字符串,再 parse 变成普通对象,不再是 Date 实例 -
Object.assign({}, new Map([['a', 1]]))→ 空对象,Map 的 entries 不暴露在 enumerable 层 -
structuredClone虽支持多数类型,但截至 2026 年仍不支持Promise、Function、Generator,且对自定义类无感知
自定义类也适用同一原则
如果你的类有不可枚举属性、Symbol 键、或需资源隔离(如内部缓存、事件监听器),就不能依赖默认拷贝。应提供显式拷贝构造逻辑:
- 在类中定义静态方法
from(obj)或实例方法clone() - 使用
Object.getOwnPropertyDescriptors()+Object.defineProperties()复制不可枚举属性和 Symbol - 对内部引用资源(如 canvas context、WebSocket)做明确判断:是共享还是新建











