深拷贝丢失原型链的本质是对象被扁平化为纯数据结构;优先用structuredclone处理内置类型,手动重建实例+递归拷贝自有属性应对自定义类,禁用json方案处理含方法或原型的对象。

深拷贝时丢失原型链,本质是对象被“扁平化”成纯数据结构,原始的 __proto__ 和构造器信息全部清空。要保留方法调用能力(比如 obj.sayHello()),关键不是复制函数本身,而是重建正确的原型关系。
优先使用 structuredClone(现代标准方案)
这是目前最可靠、原生支持原型链保留的方案——但要注意:它只对部分内置类型保留原型,不支持自定义构造函数的原型链(如 new MyClass() 拷贝后仍是普通对象)。但它能正确处理 Date、RegExp、Map、Set、ArrayBuffer 等,并天然支持循环引用。
- 适用场景:接口返回的数据含 Date/RegExp/嵌套结构,且无需调用自定义原型方法
- 不适用场景:需要拷贝
new User()实例并继续调用User.prototype.login - 写法简洁:
const cloned = structuredClone(original);
手动重建实例 + 递归拷贝自有属性
当对象明确由某个构造函数创建(如 const u = new User(...)),最稳妥的方式是:用原构造器新建实例,再把源对象的自有属性(包括 Symbol)逐个深拷贝过去。
- 先判断
obj.constructor是否存在且可调用,再执行new obj.constructor() - 用
Object.getOwnPropertyNames(obj)获取所有自有键(不含原型属性) - 用
Object.getOwnPropertySymbols(obj)补上 Symbol 键 - 对每个键值递归深拷贝(注意 Date、RegExp、数组等需单独处理)
- 避免直接改
__proto__,改用Object.setPrototypeOf(newObj, Object.getPrototypeOf(obj))
避免 JSON 方案用于含方法或原型的场景
JSON.parse(JSON.stringify(obj)) 会彻底剥离函数、Symbol、undefined、原型、getter/setter —— 它只适合“纯数据容器”,比如配置项、API 响应体。一旦对象带行为(方法)、状态(私有变量)、或依赖原型链(如继承类实例),就绝对不能用。
- 典型报错:
TypeError: obj.doSomething is not a function - 即使看起来拷贝成功,调用原型方法时也会失败
- 日期变成字符串、正则变为空对象、Map/Set 变成普通对象
第三方库的取舍:Lodash cloneDeep vs 自定义
_.cloneDeep() 默认不保留原型链,它生成的是 PlainObject;但可通过自定义定制器(customizer)干预过程,例如识别特定构造函数并手动重建实例。
- 优势:开箱即用,覆盖绝大多数边界情况(循环引用、特殊类型)
- 劣势:无法自动识别你的自定义类,需显式配置构造器映射逻辑
- 示例:对
User类,可在 customizer 中拦截,返回new User(...)并填充属性
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











