深浅拷贝是JavaScript一个非常经典的知识点,每次提到深浅拷贝我总是没办法特别清楚的表述出来,包括很多其他的知识点,其实是会的,但是没办法讲的很清楚,学习了费曼学习法,决定在复盘知识点的时候,每天找人讲解知识点,如果我能表达出来且让对方能听明白我讲的东西才算过关! !
首先简单介绍一些 深拷贝和浅拷贝,他们是JavaScript中复制对象的两种类型。
1.浅拷贝:浅拷贝只复制对象的第一层属性,而不复制嵌套对象的引用。这意味着如果原始对象包含嵌套对象,浅拷贝后的对象仍然会共享这些嵌套对象的引用。可以使用`Object.assign()`或展开运算符(`...`)来进行浅拷贝。
// 浅拷贝示例 //1.使用Object.assign const originalObj = { a: 1, b: { c: 2 } }; const shallowCopy = Object.assign({}, originalObj); //2. 使⽤展开运算符 let obj1 = { a: 1, b: 2 }; let obj2 = { ...obj1 }; console.log(obj2); // { a: 1, b: 2 }
2.深拷贝:深拷贝会递归地复制所有嵌套对象及其属性,创建一个全新的对象,不共享任何引用。这样可以确保修改深拷贝后的对象不会影响原始对象。常见的深拷贝方法包括使用`JSON.parse(JSON.stringify())`、第三方库如Lodash的`_.cloneDeep()`等。
// 深拷贝示例 const originalObj = { a: 1, b: { c: 2 } }; const deepCopy = JSON.parse(JSON.stringify(originalObj));
浅拷贝只复制对象的顶层属性
而深拷贝会递归复制所有嵌套属性,确保复制后的对象是完全独立的
以下是更深入的讲解一下深拷贝
深拷贝的几种实现方式:
1.上面的JSON.parse(JSON.stringify())
2. 使⽤递归实现深拷贝函数
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') {
return obj;
}
let clone = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key]);
}
}
return clone;
}
let obj1 = {
a: 1,
b: { c: 2 }
};
let obj2 = deepClone(obj1);
obj2.b.c = 3;
console.log(obj1.b.c); // 2
console.log(obj2.b.c); // 3
3. 使⽤第三⽅库 lodash 的 cloneDeep() ⽅法
const _ = require('lodash');
let obj1 = {
a: 1,
b: { c: 2 }
};
let obj2 = _.cloneDeep(obj1);
obj2.b.c = 3;
console.log(obj1.b.c); // 2
console.log(obj2.b.c); // 3
!!注意:其中JSON.parse(JSON.stringify())⽅法是有局限性的
- 会忽略 undefined
- 不能序列化函数
- 不能解决循环引⽤的对象
1. ⽆法处理函数: JSON.stringify() ⽅法在序列化对象时会忽略函数属性,因为函数不符合JSON 格式的数据类型。经过序列化和反序列化后,函数属性会丢失。
let obj = {
name: 'poetry',
sayHello: function() {
console.log('Hello!');
}
};
let serializedObj = JSON.stringify(obj);
let clonedObj = JSON.parse(serializedObj);
console.log(clonedObj.name); // 'poetry'
console.log(typeof clonedObj.sayHello); // 'undefined'
2. ⽆法处理循环引⽤:如果对象存在循环引⽤,即对象内部包含对⾃身的引⽤, JSON.stringify() ⽅法⽆法正确处理,会导致循环引⽤的属性被序列化为 null 。
let obj = {
name: 'poetry'
};
obj.self = obj;
let serializedObj = JSON.stringify(obj);
console.log(serializedObj); // {"name":"poetry","self":null}
3. ⽆法处理特殊对象: JSON.stringify() ⽅法⽆法序列化某些特殊对象,如 Date 对象、正则表达式、 Map 、 Set 等,它们在序列化过程中会转换成空对象。
let obj = {
now: new Date(),
regex: /[a-z]+/,
set: new Set([1, 2, 3]),
map: new Map([[1, 'one'], [2, 'two']])
};
let serializedObj = JSON.stringify(obj);
console.log(serializedObj); // {"now":{},"regex":{},"set":{},"map":{}}
4. ⽆法处理 undefined 属性: JSON.stringify() ⽅法在序列化对象时会忽略 undefined 属性,序列化后的结果不包含该属性。
let obj = {
name: 'poetry',
age: undefined
};
let serializedObj = JSON.stringify(obj);
console.log(serializedObj); // {"name":"poetry"}
在使⽤ JSON.stringify() 进⾏深拷⻉时,需要注意上述局限性,并确保对象不包含函数、循环引⽤或特殊对象,并且不需要保留 undefined 属性。对于包含上述情况的对象,应使⽤其他⽅法实现深拷⻉。