主题
深拷贝
乞丐版
js
JSON.parse(JSON.stringfy(data))
解决不能循环引用,除 Object、Array 外的类型
普通版
js
function deepClone(data){
if(data && data instanceof Object){
const obj = Array.isArray(data) ? [] : {}
for(const key in data){
obj[key] = deepClone(data[key]);
}
return obj
} else {
return data;
}
}
解决循环引用
额外开辟一个存储空间,来存储当前对象和拷贝对象的对应关系,当需要拷贝当前对象时,先去存储空间中找,有没有拷贝过这个对象,如果有的话直接返回,如果没有的话继续拷贝
js
function deepClone(data, map = new Map()) {
if(data && data instanceof Object){
const obj = Array.isArray(data) ? [] : {}
// 解决循环引用
if (map.has(data)) return map.get(data)
map.set(data, obj)
for(const key in data){
obj[key] = deepClone(data[key], map);
}
return obj
}else{
return data;
}
}
因为递归中的某一个函数结束了,就会释放函数中的变量,但如果使用 Map
,就会一直引用着,导致释放不掉,可以使用 WeakMap
优化内存
js
function deepClone(data, map = new WeakMap()) {}
识别 Map、Set、Function
首先定一个方法,识别类型
js
function getType(target) {
return Object.prototype.toString.call(target);
}
抽离要识别的类型
js
const deepTag = ['[object Object]', '[object Array]', '[object Map]', '[object Set]']
获取构造方法
js
function getInit(target) {
const Ctor = target.constructor;
return new Ctor();
}
深拷贝
js
function deepClone(data, map = new WeakMap()) {
if (!deepTag.includes(getType(data))) {
// 函数也无需深拷贝,直接返回即可(lodash 就是如此做的)
return data
}
const type = getType(target);
const obj = getInit(target);
// 防止循环引用
if (map.has(data)) return map.get(data);
map.set(target, obj);
if (type === '[object Set]') {
data.forEach(value => {
obj.add(deepClone(value, map))
})
return obj
}
if (type === '[object Map]') {
data.forEach((value, key) => {
obj.set(key, deepClone(value, map))
})
return obj
}
for (const key in data) {
obj[key] = deepClone(data[key], map)
}
return obj
}