深拷贝应显式识别类型、手动调用构造逻辑并恢复原型链,避免依赖obj.constructor;优先使用静态from()方法,辅以原型链修复、属性描述符还原及WeakMap缓存防循环引用。

深拷贝时保留原始构造函数,关键在于不能依赖 obj.constructor 直接新建实例——它在跨 iframe、子类重写、无参构造器或私有状态场景下极易出错。真正可靠的方式是:**显式识别类型 + 手动调用对应构造逻辑 + 显式恢复原型链**。
为什么 constructor 不能直接用
常见误区是写 new obj.constructor(...),但问题很多:
- 子类构造器可能要求特定参数(如
Dog(name, breed)),而原对象属性未必全量可传 -
null、Array、Date等内置类型虽有constructor,但语义不同([].constructor === Array可用,{}.constructor === Object却不总代表你想要的“业务类”) - 跨 iframe 或模块隔离时,
obj.constructor === MyClass为false,即使逻辑相同 - 箭头函数、匿名类、或使用
Object.setPrototypeOf()动态改过原型的对象,constructor可能丢失或指向错误
推荐做法:按已知类名单匹配 + 静态 from() 优先
对明确知道类型的业务类,应建立白名单,逐个处理:
- 用
obj instanceof Dog或Object.prototype.toString.call(obj) === '[object Dog]'(需提前定义Symbol.toStringTag)判断类型 - 若类提供静态
from()方法(如Dog.from(obj)),直接调用——它封装了构造细节和兼容逻辑,最可控 - 没有
from()时,手动提取关键字段重建:new Dog(obj.name, obj.breed),避免盲目遍历所有属性 - 基础类型(
Date、RegExp、Map、Set)走标准克隆路径,不依赖其constructor
必须补上原型链和不可枚举属性
仅新建实例还不够,还需还原行为一致性:
立即学习“Java免费学习笔记(深入)”;
- 用
Object.setPrototypeOf(copy, Object.getPrototypeOf(obj))显式设置原型,确保instanceof和方法查找正常 - 用
Object.getOwnPropertyDescriptors(obj)获取所有自有属性描述符(含get/set、enumerable、configurable) - 对每个描述符中的
value递归深拷贝;对get/set直接复制函数引用(它们本身不可序列化,但属于定义的一部分) - 用
Object.defineProperties(copy, descriptors)一次性还原全部描述符
循环引用与缓存机制不能少
否则多态类嵌套时会栈溢出或无限递归:
- 用
WeakMap缓存已处理的源对象 → 拷贝结果映射 - 每次进入拷贝前先查缓存:
if (cache.has(obj)) return cache.get(obj) - 创建新对象后立即缓存:
cache.set(obj, copy),保证同一对象无论在哪一层被引用,都复用同一个拷贝


















