深拷贝是指创建一个全新的对象,这个对象的所有层级属性都是原对象属性的独立副本,修改拷贝后的对象不会对原对象产生任何影响,和浅拷贝只复制第一层属性不同,深拷贝需要处理对象的所有嵌套结构。

深拷贝的核心实现思路
实现深拷贝的核心逻辑是递归遍历原对象的所有属性,针对不同类型的属性做不同的处理:
- 如果是基础数据类型,直接返回该值即可,因为基础类型本身是按值存储的,不存在引用问题
- 如果是引用类型,需要判断是数组还是普通对象,创建对应的空容器,然后递归遍历其属性进行赋值
- 如果遇到循环引用的情况,需要用一个映射表记录已经拷贝过的对象,避免递归进入无限循环
基础版深拷贝实现
先实现一个不考虑循环引用的基础版深拷贝函数,能够处理普通对象和数组:
function deepClone(target) {
// 判断是否是对象类型,基础类型直接返回
if (typeof target !== 'object' || target === null) {
return target;
}
// 判断是数组还是对象,创建对应的初始容器
const result = Array.isArray(target) ? [] : {};
// 遍历原对象的所有自有属性
for (let key in target) {
if (target.hasOwnProperty(key)) {
// 递归处理每个属性的值
result[key] = deepClone(target[key]);
}
}
return result;
}
这个函数可以处理大部分普通场景,比如拷贝普通对象、数组,但是存在一个问题,如果对象存在循环引用,会直接导致递归栈溢出。
处理循环引用的完整版深拷贝
要解决循环引用的问题,我们需要用一个WeakMap来记录已经拷贝过的对象,键是原对象,值是拷贝后的对象,每次递归前先检查映射表中是否已经有对应的拷贝结果:
function deepClone(target, cache = new WeakMap()) {
// 基础类型直接返回
if (typeof target !== 'object' || target === null) {
return target;
}
// 检查是否已经拷贝过该对象,避免循环引用
if (cache.has(target)) {
return cache.get(target);
}
// 创建对应的容器
const result = Array.isArray(target) ? [] : {};
// 先存入缓存,再递归处理,避免循环引用时找不到缓存
cache.set(target, result);
// 处理对象的自有属性
for (let key in target) {
if (target.hasOwnProperty(key)) {
result[key] = deepClone(target[key], cache);
}
}
// 处理数组的特殊属性,比如length已经自动处理,这里补充处理Symbol属性
const symbolKeys = Object.getOwnPropertySymbols(target);
for (let symKey of symbolKeys) {
result[symKey] = deepClone(target[symKey], cache);
}
return result;
}
深拷贝的边界情况处理
上面的实现已经能处理大部分场景,但是如果需要拷贝特殊对象比如Date、RegExp、Map、Set等,还需要额外增加类型判断:
function deepClone(target, cache = new WeakMap()) {
// 基础类型直接返回
if (typeof target !== 'object' || target === null) {
return target;
}
// 检查缓存
if (cache.has(target)) {
return cache.get(target);
}
// 处理特殊对象类型
if (target instanceof Date) {
return new Date(target.getTime());
}
if (target instanceof RegExp) {
return new RegExp(target.source, target.flags);
}
if (target instanceof Map) {
const map = new Map();
cache.set(target, map);
target.forEach((value, key) => {
map.set(deepClone(key, cache), deepClone(value, cache));
});
return map;
}
if (target instanceof Set) {
const set = new Set();
cache.set(target, set);
target.forEach(value => {
set.add(deepClone(value, cache));
});
return set;
}
// 普通对象和数组的处理
const result = Array.isArray(target) ? [] : {};
cache.set(target, result);
// 处理普通属性
for (let key in target) {
if (target.hasOwnProperty(key)) {
result[key] = deepClone(target[key], cache);
}
}
// 处理Symbol属性
const symbolKeys = Object.getOwnPropertySymbols(target);
for (let symKey of symbolKeys) {
result[symKey] = deepClone(target[symKey], cache);
}
return result;
}
深拷贝实现注意事项
- 使用
WeakMap而不是普通对象来存储缓存,是因为WeakMap的键是弱引用,不会阻止垃圾回收,避免内存泄漏 - 函数类型的属性一般不需要深拷贝,直接赋值即可,因为函数本身是可复用的,拷贝函数没有实际意义,如果需要拷贝函数,可以通过
eval或者new Function实现,但是会损失闭包上下文 - 如果对象包含不可枚举的属性,上面的实现无法拷贝,需要结合
Object.getOwnPropertyNames来获取所有属性名再处理 - 实际开发中如果不想自己实现,也可以使用成熟的库比如lodash的
cloneDeep方法,其内部已经处理了各种边界情况
深拷贝JavaScript对象复制递归遍历修改时间:2026-06-25 16:51:36