当你复制一个对象时,浅拷贝和深拷贝指的是复制过程中是否复制了对象内部的引用。
浅拷贝(Shallow Copy)
浅拷贝创建了一个新对象,这个新对象有着原始对象属性值的一份精确拷贝。但对于原始对象中的嵌套对象或数组等引用类型的属性,浅拷贝只会复制它们的引用而不是创建新的引用。因此,如果原始对象内的引用类型属性发生变化,浅拷贝后的对象也会受到影响。
const obj = { a: 1, b: { c: 2 } };
// 浅拷贝
const shallowCopy = Object.assign({}, obj);
obj.a = 100; // 修改原对象属性值
obj.b.c = 200; // 修改原对象中的对象属性值
console.log(shallowCopy); // 输出: { a: 1, b: { c: 200 } },原对象属性变化会影响浅拷贝
在上面的例子中,shallowCopy
是 obj
的浅拷贝,当修改了 obj
的属性值时,shallowCopy
也受到了影响,因为它们共享相同的引用类型属性。
深拷贝(Deep Copy)
深拷贝创建了一个全新的对象,它将原始对象内部的所有属性都复制了一份,包括嵌套对象和数组等引用类型的属性。换句话说,深拷贝不仅复制了对象本身,还会递归地复制所有引用类型的属性,确保创建的对象与原始对象完全独立,互不影响。
const obj = { a: 1, b: { c: 2 } };
// 深拷贝
const deepCopy = JSON.parse(JSON.stringify(obj));
obj.a = 100; // 修改原对象属性值
obj.b.c = 200; // 修改原对象中的对象属性值
console.log(deepCopy); // 输出: { a: 1, b: { c: 2 } },原对象属性变化不会影响深拷贝
在这个例子中,deepCopy
是 obj
的深拷贝。即使修改了 obj
的属性值,deepCopy
也不受影响,因为它们是独立的对象。JSON.parse(JSON.stringify(obj))
是一个简单的深拷贝方法,但它有一些限制,无法复制函数、正则表达式等特殊类型的属性。
浅拷贝对象
使用 Object.assign()
const obj = { a: 1, b: { c: 2 } };
// 使用 Object.assign() 进行浅拷贝
const shallowCopy = Object.assign({}, obj);
obj.a = 100; // 修改原对象属性值
obj.b.c = 200; // 修改原对象中的对象属性值
console.log(shallowCopy); // 输出: { a: 1, b: { c: 200 } },原对象属性变化会影响浅拷贝
使用扩展运算符 ...
const obj = { a: 1, b: { c: 2 } };
// 使用扩展运算符进行浅拷贝
const shallowCopy = { ...obj };
obj.a = 100; // 修改原对象属性值
obj.b.c = 200; // 修改原对象中的对象属性值
console.log(shallowCopy); // 输出: { a: 1, b: { c: 200 } },原对象属性变化会影响浅拷贝
无论使用哪种方式,它们都能够创建一个新对象,并复制原对象的属性。但需要注意的是,如果原对象的属性值是引用类型(如对象或数组),浅拷贝只会复制引用,而不是创建新的引用。这意味着对于原对象中的嵌套对象或数组,修改它们的属性值会影响浅拷贝后的对象。
手动复制属性
const obj = { a: 1, b: { c: 2 } };
const shallowCopy = {};
for (let key in obj) {
shallowCopy[key] = obj[key];
}
obj.a = 100; // 修改原对象属性值
obj.b.c = 200; // 修改原对象中的对象属性值
console.log(shallowCopy); // 输出: { a: 1, b: { c: 200 } },原对象属性变化会影响浅拷贝
这种方法使用 for...in
循环遍历原对象的属性,并将其复制到新对象中。然而,和其他浅拷贝方式一样,对于原对象中的引用类型属性,仅复制了引用而不是创建新的引用。
使用 Object.create()
const obj = { a: 1, b: { c: 2 } };
const shallowCopy = Object.create(obj);
obj.a = 100; // 修改原对象属性值
obj.b.c = 200; // 修改原对象中的对象属性值
console.log(shallowCopy); // 输出: {},原对象属性变化不会影响浅拷贝
Object.create()
方法创建了一个新对象,并将原对象作为新对象的原型。这样创建的新对象是一个空对象,并不直接包含原对象的属性。对原对象的属性的更改不会影响到使用 Object.create()
创建的新对象,但是需要注意,它只能复制原对象的原型链上的属性。
上述方法比较常见,也有别的方法就不一一例举了!!
浅拷贝一个单层对象通常可以使用 Object.assign()
或者 扩展运算符 ...
深拷贝对象
使用 JSON 方法(适用于部分场景)
const obj = { a: 1, b: { c: 2 } };
// 使用 JSON 方法进行深拷贝
const deepCopy = JSON.parse(JSON.stringify(obj));
obj.a = 100; // 修改原对象属性值
obj.b.c = 200; // 修改原对象中的对象属性值
console.log(deepCopy); // 输出: { a: 1, b: { c: 2 } },原对象属性变化不会影响深拷贝
使用递归实现深拷贝
function deepClone(obj, clonedObjects = new WeakMap()) {
if (obj === null || typeof obj !== 'object') {
return obj; // 如果是原始值或者 null,直接返回
}
// 避免循环引用
if (clonedObjects.has(obj)) {
return clonedObjects.get(obj);
}
// 处理对象或数组
const clone = Array.isArray(obj) ? [] : {};
// 将新对象存入 WeakMap,避免循环引用导致的无限递归
clonedObjects.set(obj, clone);
// 递归复制每个属性
for (let key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
clone[key] = deepClone(obj[key], clonedObjects);
}
}
return clone;
}
这个 deepClone
函数可以递归地复制对象及其嵌套属性,确保创建的是原对象的完全独立拷贝。这种方法相对较为复杂,但能够处理对象中的各种情况,包括嵌套对象和数组的深度拷贝。
lodash
库中的 _.cloneDp()ee
方法
除了使用递归和 JSON 方法外,还有其他一些库可以帮助进行深拷贝,比如 lodash
库中的 _.cloneDp()ee
方法。这个方法可以深度复制对象和数组,并处理循环引用。
npm install lodash
然后,使用 _.cloneDeep()
方法进行深拷贝:
const _ = require('lodash');
const obj = { a: 1, b: { c: 2 } };
// 使用 _.cloneDeep() 进行深拷贝
const deepCopy = _.cloneDeep(obj);
obj.a = 100; // 修改原对象属性值
obj.b.c = 200; // 修改原对象中的对象属性值
console.log(deepCopy); // 输出: { a: 1, b: { c: 2 } },原对象属性变化不会影响深拷贝
lodash
库提供了许多实用的函数,其中 _.cloneDeep()
是一个处理深度拷贝的方便方法。它可以帮助你避免编写自己的深拷贝函数,并提供了更多的功能来处理对象的拷贝。