深拷贝(deep copy)是指在内存中创建一个完全独立的新对象,并将原对象的所有内容复制到新对象中。相比之下,浅拷贝(shallow copy)只是复制对象的引用,而不是复制对象本身。深拷贝在以下场景中非常有用:
防止引用共享问题:当多个对象引用同一个对象时,如果对其中一个对象做出修改,会影响其他对象。使用深拷贝可以创建一个完全独立的对象,避免这种共享问题。
对象状态快照:有时候我们需要对对象的状态进行快照,以便将来能够回滚到特定的状态。深拷贝可以用于创建状态的副本,而不影响原始对象。
复制可变对象:如果一个对象包含其他可变对象作为其属性,而你需要复制整个对象图,以便修改副本而不会影响原始对象,这时深拷贝非常有用。
状态管理:当使用状态管理库(如Redux、Vuex)时,需要对状态进行深拷贝以避免直接修改原始状态。这样可以确保状态变更的可追溯性和可控性。
表单数据处理:当从表单中获取数据时,通常需要对数据进行深拷贝。这是因为对象是通过引用传递的,若直接修改表单数据,可能会引发不可预料的问题。通过深拷贝,可以在不修改原始数据的情况下对数据进行处理和验证。
缓存数据处理:当从缓存中获取对象并进行修改时,使用深拷贝可以避免修改缓存中的原始数据。这对于对缓存进行更新、比较或存储历史数据非常有用。
避免对象引用共享问题:在前端开发中,多个对象可能引用同一个对象。如果对其中一个对象进行修改,会影响其他对象。深拷贝可以创建独立的对象副本,避免引用共享问题。
对象传递和传输:在前端开发中,有时候需要将对象传递给其他组件、模块或服务。为了确保传递的对象不受外部修改的影响,可以使用深拷贝进行对象的传递和传输。
function deepClone(obj, cache = new WeakMap()) {
// 缓存一个map结构, WeakMap不影响垃圾回收, 该回收就回收掉了,可以减少内存泄漏的风险
/**
* WeakMap 中的键只能是对象,并且对于键对象的引用是弱引用,
* 这意味着当键对象不再被引用时,它们将被垃圾回收。
*/
if (obj === null || typeof obj !== "object") {
return obj;
}
// 处理正则表达式
if (obj instanceof RegExp) {
return new RegExp(obj);
}
// 处理日期
if (obj instanceof Date) {
return new Date(obj);
}
// 递归之前先判断一下, 检查是否已经拷贝过该对象
if (cache.has(obj)) {
return cache.get(obj);
}
const clone = Array.isArray(obj) ? [] : {};
// 没有缓存情况,将克隆对象添加到缓存
cache.set(obj, clone);
for (const key in obj) {
// 排除原型上的属性
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key], cache);
}
}
return clone;
}
const obj = {
arr: [1, 2, 3],
a: 4,
c: /{1, 5}/,
d: function hello() {
console.log("say Hello!");
},
e: new Date(),
f: undefined,
g: null,
};
obj.sub = obj;
obj.arr.push(obj);
const newObj = deepClone(obj);
console.log(newObj);
console.log(newObj.arr !== obj.arr); // true
console.log(newObj.sub !== obj.sub); // true
console.log(newObj.arr[3] !== obj); // true, push的新数组不等于原来的对象
console.log(newObj.arr[3] === newObj); // true, 等于自身的对象