javascript带原型深拷贝需重建原型链并递归复制自有属性:用object.create(object.getprototypeof(obj))还原原型,object.keys()遍历自有可枚举属性,weakmap防循环引用,函数等不可序列化类型应明确放弃或报错。

JavaScript 中处理复杂对象的深拷贝,若还需保留原型链(比如自定义类实例、带方法的对象、或继承自特定 prototype 的对象),就不能只靠 JSON.parse(JSON.stringify()) 或 structuredClone —— 它们会丢失原型,克隆体一律指向 Object.prototype。真正要“带原型的深拷贝”,关键在于两件事:重建原型链 + 递归复制自有属性。
1. 先还原原型,再填内容
原型是对象的“父辈关系”,决定了 instanceof、方法查找、isPrototypeOf 是否成立。直接用 Object.assign 或展开运算符,新对象的 __proto__ 默认是 Object.prototype,和原对象完全脱节。
- 用
Object.getPrototypeOf(obj)获取原对象的原型 - 用
Object.create(proto)创建一个空对象,并指定其原型为该 proto - 对
null原型(如Object.create(null))要单独判断,传入null作为create参数
2. 只拷贝自有可枚举属性,避免污染继承链
原型上的方法或属性不该被复制到克隆体自身,否则会破坏语义(比如把 toString 变成自有属性)。所以遍历时必须用 Object.keys() 或 for...in + hasOwnProperty,确保只处理对象自身的可枚举属性。
- 基本类型(string/number/boolean/null/undefined/symbol/bigint)直接返回
-
Date→new Date(obj.getTime()) -
RegExp→new RegExp(obj.source, obj.flags) -
Array→[]新建,再逐项递归拷贝 - 普通对象(非内置类)→ 递归调用同一克隆函数
-
Map/Set/TypedArray等需显式支持,不能靠new obj.constructor()(可能无参报错或执行副作用)
3. 必须防循环引用,否则栈溢出
如果对象内部存在自引用(如 a.b = a)或环状结构(a.ref = b; b.ref = a),纯递归会无限深入。解决方案是用 WeakMap 缓存已处理的对象对:
- 每次进入克隆前,先查
WeakMap是否已有该原对象 → 有则直接返回对应克隆体 - 创建新克隆体后,立即
map.set(original, clone),再开始遍历属性 -
WeakMap键只能是对象,且不阻止垃圾回收,比普通Map更安全
4. 不支持的类型要明确放弃
函数、Promise、Error、DOM 节点、WebAssembly 实例 等不可序列化对象,无法真正“拷贝行为”。强行尝试(如 obj.constructor)可能抛错或产生意外状态。稳妥做法是:遇到这些类型时直接返回原值(浅拷贝语义),或抛错提示用户 —— 比静默失败更可靠。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











