JavaScript 深拷贝:手写一个简单可靠的 `deepClone` 函数
1. 为什么需要这个 deepClone
函数?
在 JavaScript 中,直接赋值对象或数组时,只是复制了引用(浅拷贝),修改新对象会影响原对象。例如:
const original = { a: 1, b: { c: 2 } };
const shallowCopy = original; // 浅拷贝
shallowCopy.b.c = 3;
console.log(original.b.c); // 3(原对象被意外修改)
深拷贝(Deep Clone) 可以彻底复制对象及其嵌套结构,使新旧对象完全独立。
2. deepClone
函数解析
function deepClone(obj) {
// 1. 处理基本类型(null, number, string, boolean, undefined, symbol)
if (obj === null || typeof obj !== 'object') {
return obj;
}
// 2. 处理 Date 对象
if (obj instanceof Date) {
return new Date(obj);
}
// 3. 处理 RegExp 对象
if (obj instanceof RegExp) {
return new RegExp(obj);
}
// 4. 初始化克隆对象(数组或普通对象)
const cloneObj = Array.isArray(obj) ? [] : {};
// 5. 递归拷贝所有属性
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
cloneObj[key] = deepClone(obj[key]);
}
}
return cloneObj;
}
关键点解析
-
基本类型直接返回
null
、number
、string
、boolean
、undefined
、Symbol
等直接返回,因为它们是不可变的。 -
处理
Date
和RegExp
这两种对象需要特殊处理,否则直接赋值会导致引用共享。 -
递归拷贝对象属性
遍历对象的所有自有属性(hasOwnProperty
过滤原型链属性),递归调用deepClone
确保嵌套结构也被复制。
3. 使用示例
const original = {
name: "Alice",
age: 25,
birthDate: new Date("1998-05-20"),
skills: ["JavaScript", "React"],
address: {
city: "Beijing",
zip: "100000"
},
regex: /hello/gi
};
const cloned = deepClone(original);
// 修改克隆对象,不影响原对象
cloned.age = 26;
cloned.skills.push("Vue");
cloned.address.city = "Shanghai";
console.log(original.age); // 25(未受影响)
console.log(original.skills); // ["JavaScript", "React"](未受影响)
console.log(original.address.city); // "Beijing"(未受影响)
4. 适用场景
✅ 普通对象、数组、Date
、RegExp
✅ 嵌套结构(多层对象/数组)
✅ 不需要处理循环引用的场景
5. 局限性
❌ 不支持循环引用(如 a.self = a
,会导致栈溢出)
❌ 不处理 Map
、Set
、WeakMap
、WeakSet
❌ 不拷贝原型链上的属性
6. 如何改进?
如果需要更强大的深拷贝:
- 处理循环引用:使用
WeakMap
缓存已拷贝对象(增强版实现)。 - 支持更多类型:如
Map
、Set
、Blob
等。 - 性能优化:改用迭代方式避免递归栈溢出。
7. 总结
这个 deepClone
函数:
- 代码简洁(仅 15 行)
- 覆盖大部分常见需求(对象、数组、
Date
、RegExp
) - 无第三方依赖(纯原生 JS 实现)
推荐在简单场景下使用,复杂数据建议用 lodash.cloneDeep
或完整版深拷贝。
你平时用什么方式实现深拷贝?欢迎讨论! 🚀