在JavaScript中,深拷贝(Deep Copy) 与 浅拷贝(Shallow Copy) 是处理对象和数组复制时非常重要的概念。理解它们的区别,对于避免数据意外修改、提高代码健壮性至关重要。
一、基本概念
1. 浅拷贝(Shallow Copy)
- 只复制对象的第一层属性。
- 对于对象中的基本数据类型(如
string
、number
、boolean
),会复制值。 - 对于对象中的引用类型(如对象、数组),只复制引用地址,而不是创建新的对象。
- 因此,原对象和拷贝对象共享嵌套对象的引用,修改嵌套对象会影响彼此。
2. 深拷贝(Deep Copy)
- 递归复制对象的所有层级。
- 不仅复制第一层,还会深入复制所有嵌套的对象和数组。
- 原对象和拷贝对象完全独立,互不影响。
二、示例对比
示例:浅拷贝
const original = {name: "Alice",info: {age: 25,hobbies: ["reading", "coding"]}
};// 浅拷贝方法1:使用 Object.assign
const shallowCopy = Object.assign({}, original);// 浅拷贝方法2:使用展开运算符
const shallowCopy2 = { ...original };// 修改浅拷贝的嵌套对象
shallowCopy.info.age = 30;
shallowCopy.name = "Bob";console.log(original.name); // "Alice" ✅(基本类型不受影响)
console.log(original.info.age); // 30 ❌(引用类型被修改了!)
⚠️ 结论:修改
shallowCopy.info.age
影响了原对象,因为info
是引用类型,只复制了指针。
示例:深拷贝
const original = {name: "Alice",info: {age: 25,hobbies: ["reading", "coding"]}
};// 方法1:JSON 序列化(简单但有限制)
const deepCopy1 = JSON.parse(JSON.stringify(original));// 方法2:递归函数实现(更灵活)
function deepClone(obj) {if (obj === null || typeof obj !== 'object') return obj;if (obj instanceof Date) return new Date(obj);if (obj instanceof Array) return obj.map(item => deepClone(item));if (typeof obj === 'object') {const cloned = {};for (let key in obj) {if (obj.hasOwnProperty(key)) {cloned[key] = deepClone(obj[key]);}}return cloned;}
}const deepCopy2 = deepClone(original);// 修改深拷贝
deepCopy2.info.age = 30;
deepCopy2.name = "Bob";console.log(original.name); // "Alice" ✅
console.log(original.info.age); // 25 ✅(未受影响)
✅ 深拷贝后,修改拷贝对象不会影响原对象。
三、常见拷贝方法分析
方法 | 类型 | 是否深拷贝 | 局限性 |
---|---|---|---|
Object.assign() |
浅拷贝 | ❌ | 只复制第一层 |
{...obj} 展开语法 |
浅拷贝 | ❌ | 同上 |
Array.prototype.slice() |
浅拷贝 | ❌ | 数组专用,嵌套对象仍共享 |
JSON.parse(JSON.stringify(obj)) |
深拷贝 | ✅ | 无法处理函数、undefined、Symbol、循环引用、Date 特殊对象 |
递归函数 | 深拷贝 | ✅ | 灵活,可自定义处理类型 |
structuredClone() (现代浏览器) |
深拷贝 | ✅ | 支持大多数类型(包括 Date、RegExp),但不支持函数 |
四、推荐深拷贝方案
✅ 推荐1:使用 structuredClone()
(现代标准)
const original = { name: "Alice", info: { age: 25 } };
const deepCopy = structuredClone(original);
✅ 支持:Date、RegExp、Map、Set、Array、对象等
❌ 不支持:函数、Error 对象、DOM 节点
✅ 推荐2:自定义深拷贝函数(处理复杂需求)
function deepClone(obj, hash = new WeakMap()) {if (obj === null || typeof obj !== 'object') return obj;if (hash.has(obj)) return hash.get(obj); // 处理循环引用let cloned;if (obj instanceof Date) cloned = new Date(obj);else if (obj instanceof RegExp) cloned = new RegExp(obj);else if (obj instanceof Array) cloned = obj.map(item => deepClone(item, hash));else if (typeof obj === 'object') {cloned = {};hash.set(obj, cloned); // 记录已克隆对象for (let key in obj) {if (obj.hasOwnProperty(key)) {cloned[key] = deepClone(obj[key], hash);}}}return cloned;
}
✅ 支持循环引用、多种内置对象
五、什么时候用深拷贝?什么时候用浅拷贝?
场景 | 建议 |
---|---|
只读数据、简单对象 | 浅拷贝足够 |
需要修改嵌套结构 | 深拷贝 |
状态管理(如 Redux) | 深拷贝或使用不可变更新 |
表单数据备份 | 深拷贝更安全 |
性能敏感场景 | 浅拷贝 + 按需深拷贝 |
六、总结:一句话区别
浅拷贝:只复制表面,嵌套对象共用;
深拷贝:里里外外全复制,完全独立。
七、常见误区
- ❌
const copy = obj
不是拷贝,只是引用赋值。 - ❌
JSON.parse(JSON.stringify(obj))
不能处理函数和循环引用。 - ✅ 深拷贝性能开销大,不要滥用。
✅ 最佳实践建议:
- 优先使用
structuredClone()
(现代项目) - 复杂场景使用自定义深拷贝
- 简单场景可用浅拷贝 + 注意嵌套修改
通过理解深浅拷贝,你可以更好地控制数据状态,避免“改了一个地方,其他地方莫名其妙变了”的 bug!