javascript中实现对象全深拷贝的最优方式是递归,需识别引用类型、避免循环引用、处理date/regexp/map/set等特殊对象,并用weakmap缓存防止栈溢出。

JavaScript 中实现对象的全深拷贝,递归是最直观、可控性最强的方式之一。关键在于:正确识别引用类型、避免循环引用、处理特殊对象(如 Date、RegExp、Map、Set 等),并为每种类型提供对应的拷贝逻辑。
基础递归深拷贝(支持普通对象和数组)
最简版本适用于纯 JSON-like 数据(即只含 null、undefined、基本类型、普通对象、数组):
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj;
const cloned = Array.isArray(obj) ? [] : {};
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
cloned[key] = deepClone(obj[key]);
}
}
return cloned;
}
说明:用 typeof obj === 'object' 和 Array.isArray 区分数组与对象;递归调用自身处理每个属性值。
处理常见内置类型(Date、RegExp、Map、Set)
原生类型需单独判断并构造新实例:
-
Date →
new Date(obj.getTime())或new Date(obj) -
RegExp →
new RegExp(obj.source, obj.flags) -
Map → 遍历
obj.entries(),逐对拷贝键值 -
Set → 用
Array.from(obj).map(deepClone)再构造新 Set
示例片段:
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags);
if (obj instanceof Map) {
return new Map(Array.from(obj, ([k, v]) => [deepClone(k), deepClone(v)]));
}
if (obj instanceof Set) {
return new Set(Array.from(obj, deepClone));
}
防止循环引用(使用 WeakMap 缓存已拷贝对象)
若对象存在自引用或相互引用,不加控制会无限递归导致栈溢出。解决方案是用 WeakMap 记录原始对象 → 克隆后对象的映射:
- 每次进入函数先查 WeakMap,命中则直接返回缓存结果
- 新建克隆对象后立即存入 WeakMap,再递归处理属性
完整结构示意:
function deepClone(obj, cache = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (cache.has(obj)) return cache.get(obj);
let cloned;
if (obj instanceof Date) cloned = new Date(obj);
else if (obj instanceof RegExp) cloned = new RegExp(obj.source, obj.flags);
else if (obj instanceof Map) {
cloned = new Map();
cache.set(obj, cloned);
obj.forEach((v, k) => cloned.set(deepClone(k, cache), deepClone(v, cache)));
} else if (obj instanceof Set) {
cloned = new Set();
cache.set(obj, cloned);
obj.forEach(v => cloned.add(deepClone(v, cache)));
} else {
cloned = Array.isArray(obj) ? [] : {};
cache.set(obj, cloned);
Object.keys(obj).forEach(key => {
cloned[key] = deepClone(obj[key], cache);
});
}
return cloned;
}
注意事项与边界情况
递归深拷贝无法完美覆盖所有 JS 类型:
-
函数、Symbol、BigInt、Promise、Error、HTML 元素等不会被拷贝或丢失行为 —— 函数默认被忽略(浅拷贝引用),Symbol 键需用
Object.getOwnPropertySymbols()显式获取 - 不可枚举属性、原型链上的属性不会被复制 —— deepClone 只处理自有可枚举属性
- 性能敏感场景慎用深度递归 —— 超深嵌套对象可能触发调用栈限制(可用栈模拟替代递归)
实际项目中,若需求明确,建议优先使用成熟库如 lodash.cloneDeep;若需轻量定制,以上递归结构已覆盖绝大多数业务场景。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











