浅拷贝和深拷贝是复制对象时常用的两种方式。它们的主要区别在于,拷贝对象的层级结构和引用关系。
好的,让我再详细解释一下浅拷贝和深拷贝的区别,并列举所有常见的 深拷贝方法。
浅拷贝(Shallow Copy)
浅拷贝指的是创建一个新对象,但该对象仅复制原对象的第一层属性,如果属性是基本数据类型(如字符串、数字等),会复制其值;如果属性是引用类型(如数组、对象等),则复制的是引用,两个对象中的引用类型属性指向同一个内存地址。
浅拷贝的关键点在于,它只会复制对象的顶层内容,对于深层次的引用类型属性,还是共享同一个地址。
如下代码所示:
const obj1 = { a: 1, b: [2, 3] };
const obj2 = Object.assign({}, obj1); // 浅拷贝
// 修改 obj2 中的数组
obj2.b.push(4);
console.log(obj1.b); // [2, 3, 4],obj1 也被修改了
console.log(obj2.b); // [2, 3, 4]
最终输出结果如下图所示:
浅拷贝方法如 Object.assign()
和展开运算符(...
)只复制对象的第一层,对于引用类型的属性,会复制引用而非实际数据,因此修改新对象中的引用类型属性可能会影响原对象。需要注意的是,for...in
用于遍历对象的属性名,而非属性值。
深拷贝(Deep Copy)
深拷贝指的是递归地复制对象及其所有嵌套对象。它会创建新对象,并确保新对象与原对象完全独立,深拷贝不会共享任何引用类型的内存地址。
深拷贝的关键在于,所有对象的嵌套属性都会被复制一份,且引用类型的属性指向新对象的内存地址,而非原对象。
如下代码代码所示:
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = JSON.parse(JSON.stringify(obj1)); // 深拷贝
obj2.b.c = 3;
console.log(obj1.b.c); // 2,原对象不受影响
console.log(obj2.b.c); // 3
最终输出结果如下图所示:
JSON.parse(JSON.stringify(obj))
通过将对象转为 JSON 字符串再解析为新对象实现深拷贝,但此方法只能处理简单数据类型,无法处理 undefined
、function
、Date
、Map
、Set
、RegExp
等复杂类型,也无法处理循环引用。
const obj1 = { a: 1, b: [2, 3], d: new Date() };
const obj2 = JSON.parse(JSON.stringify(obj1));
obj2.b.push(4);
console.log(obj1.b); // [2, 3],原对象不受影响
console.log(obj2.b); // [2, 3, 4]
console.log(obj1.d instanceof Date); // true,Date 不会被复制
递归自定义深拷贝
自定义的深拷贝函数通过递归地遍历对象的每一层,逐层复制原对象的所有值,确保对象完全独立。
function deepCopy(obj) {
if (obj === null || typeof obj !== 'object') return obj; // 基础数据类型直接返回
const newObj = Array.isArray(obj) ? [] : {}; // 创建新对象或数组
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = deepCopy(obj[key]); // 递归复制
}
}
return newObj;
}
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = deepCopy(obj1);
obj2.b.c = 3;
console.log(obj1.b.c); // 2,原对象不受影响
console.log(obj2.b.c); // 3
structuredClone()
structuredClone()
是浏览器提供的原生方法,可以深拷贝大多数 JavaScript 对象,包括 Date
、Map
、Set
等,且比 JSON.parse(JSON.stringify())
更强大,能够正确处理更多数据类型。
const obj1 = { a: 1, b: { c: 2 }, d: new Date() };
const obj2 = structuredClone(obj1);
obj2.b.c = 3;
console.log(obj1.b.c); // 2,原对象不受影响
console.log(obj2.b.c); // 3
console.log(obj1.d instanceof Date); // true,Date 被正确复制
如下图所示:
深拷贝 vs 浅拷贝
特性 | 浅拷贝 | 深拷贝 |
---|---|---|
拷贝层级 | 仅拷贝第一层对象 | 递归拷贝所有层级的对象 |
引用类型处理 | 引用类型属性仍指向原对象 | 引用类型属性被完全拷贝,指向新对象 |
性能 | 快于深拷贝 | 相对较慢,特别是在对象嵌套较深时 |
适用场景 | 适用于对象结构简单,且不涉及深层引用的情况 | 适用于需要完全独立的副本,避免引用关系共享 |
总结
深拷贝适用于需要完全独立副本的情况,尤其是对象包含复杂类型或多层嵌套时。浅拷贝则适用于对象没有嵌套引用类型,或者只需复制对象的第一层属性时。
浅拷贝只复制对象的第一层,对于引用类型,复制的是内存地址,导致对象之间共享引用类型的数据;深拷贝则递归复制所有层级,确保对象完全独立,常用方法包括 JSON.parse(JSON.stringify())
、递归自定义深拷贝和 structuredClone()
。