在 JavaScript 中,浅拷贝 和 深拷贝 的主要区别在于它们如何处理对象中的嵌套对象(即对象的属性也是对象的情况)。
1. 浅拷贝:
浅拷贝只复制对象的第一层属性。如果对象的属性是一个引用类型(如对象或数组),浅拷贝只会复制该引用的地址,而不是实际的值。这意味着,如果你修改了拷贝对象中引用类型的属性,原对象中的该属性也会被修改。
常见的浅拷贝方式:
Object.assign()
- 扩展运算符
...
let obj1 = { a: 1, b: { c: 2 } };
let obj2 = Object.assign({}, obj1);
// 或者
let obj3 = { ...obj1 };
// 修改 obj2 中的 b 属性
obj2.b.c = 3;
console.log(obj1.b.c); // 输出 3,因为 obj1 和 obj2 共享同一个 b 对象的引用
2. 深拷贝:
深拷贝会递归地复制对象的所有层次,包括嵌套的对象和数组。这意味着拷贝后的对象和原对象完全独立,修改拷贝对象的任意部分都不会影响原对象。
实现深拷贝的常见方法:
1. 使用 JSON.stringify()
和 JSON.parse()
:
这是一种简单但不太灵活的方法,它会将对象序列化为 JSON 字符串,再解析为新对象。它有一些局限性,比如不能处理 undefined
、函数、循环引用等复杂数据结构。
let obj1 = { a: 1, b: { c: 2 } };
let obj2 = JSON.parse(JSON.stringify(obj1));
// 修改 obj2 中的 b 属性
obj2.b.c = 3;
console.log(obj1.b.c); // 输出 2,因为 obj1 和 obj2 是完全独立的
2. 递归实现深拷贝:
为了实现更健壮的深拷贝,可以手动递归地复制对象的每一层属性。
function deepClone(obj) {
// 如果不是对象或者是 null,直接返回原值
if (typeof obj !== "object" || obj === null) {
return obj;
}
// 创建一个新的对象或数组,取决于原始对象的类型
let newObj = Array.isArray(obj) ? [] : {};
for (let key in obj) {
// 确保该属性是对象自己的(而不是继承的)
if (obj.hasOwnProperty(key)) {
// 递归地拷贝属性值
newObj[key] = deepClone(obj[key]);
}
}
return newObj;
}
let obj1 = { a: 1, b: { c: 2 } };
let obj2 = deepClone(obj1);
obj2.b.c = 3;
console.log(obj1.b.c); // 输出 2,拷贝是深层的
3. 使用 Lodash 库的 cloneDeep
方法:
Lodash 是一个流行的 JavaScript 工具库,它提供了一个强大的深拷贝方法 _.cloneDeep()
,可以处理复杂的数据结构。
let _ = require('lodash'); // 引入 lodash 库
let obj1 = { a: 1, b: { c: 2 } };
let obj2 = _.cloneDeep(obj1);
obj2.b.c = 3;
console.log(obj1.b.c); // 输出 2
深拷贝和浅拷贝的应用场景:
- 浅拷贝 适合简单的、没有嵌套对象的数据结构。
- 深拷贝 在处理包含复杂对象、嵌套对象或数组时更为合适,因为这确保了数据的独立性。
总结:
- 浅拷贝:只复制第一层的属性,嵌套对象引用仍然指向原来的对象。
- 深拷贝:递归复制所有层次的属性,拷贝后的对象与原对象完全独立。