本文分三部分
1.先说什么是深复制浅复制 在定义上进行区分
2.深复制浅复制的各个应用场景
3.总结浅复制深复制优缺点
4.如何实现深复制
5.json实现深复制的注意事项
一.深复制浅复制的定义概念区分
什么是浅复制?
文字定义:就是原对象里有属性也是一个对象,当在复制时,这个对象属性引用值没有发生变化,还是指向原对象的对象属性。
代码表示:
const originObj={a:1,objAttr:{b:2}};
const newObj={...originObj};
newObj.objAttr===originObj.objAttr///比较结果是true, newObj.objAttr对象引用值没有变化,还是指向原对象属性
什么是深复制
文字定义:就是原对象里有属性也是一个对象,当在复制时,这个对象属性引用值发生变化被赋予新值,不指向原对象的对象属性。
代码表示:
const originObj={a:1,objAttr:{b:2}};
const newObj=JSON.parse(JSON.stringify(orginObj));
newObj.objAttr===originObj.objAttr///比较结果是false, newObj.objAttr对象引用值变化,不指向原对象属性
二.深复制浅复制的应用场景
深度复制应用场景
深复制 主要作用是隔离两个相关变量的相互影响,有哪些场景要注意隔离影响呢
1.常规就是函数传递参数时,隔离相互影响;函数调用要传递一个对象数据时,养成深克隆后传递的习惯;不要希望别人没有修改你的原对象;另外代码会更加稳定。多人协作写代码,可能此刻是好的,下一个月就有人修改了你调用的函数,修改了你传递进来的对象。导致莫名错误。
如下面代码
function mdy(obj){
obj.subObj.xx=123;//函数内部修改了传递进来的对象 如果是你是深克隆后传递进来 就不用超心。
}
2.在react的hook里 如useEffect 依赖一个状态是对象时,只有对象引用发生变化,这个useEffect才会执行
const [stateData,setStateData]= useState({obj:{},xx:3})
useEffect(()=>{
//do something
},[ stateData.obj ] );//这个时候 stateData每次设置就必须是深克隆
在react里应用还很多的,例如是否应该更新渲染 的判断 比较的也是引用是否发生了变化。另外全局状态的追踪记录 也是引用比较。
在对象要进行引用比较时 深克隆比较安全
浅复制应用场景
1.两种复制方式都能解决问题时,就采用浅复制,因为浅复制性能更好。
什么情况下会出现浅复制解决问题就好
比如下面代码
const obj={a:1,b:{c:3}}
//你关注的值只有a时,不关注b时,就采用浅复制。即只关心基本类型的数据,不关心引用类型的数据时。采用浅复制
2.必须采用浅复制的场景
就是对象之间可以共享一个公共对象,通过共享公共对象可以节省内存,也更好的实现协同。
例如一般每个学生都有一个相同的班主任。这个时候通过浅复制学生对象再生产出一个学生对象,两个学生对象之间就能够共享班主任对象了。 代码表示如下
const stdA={age:10,name:'a' sex:boy,headTeacher:{name:"liwei",sex:'gril' }};
const stdB={...stdA};//通过浅复制出来学生b就和学生a有了相同的班主任。
stdB.age=12;
stdB.name='b'
如果有成千上万个学生共享一个校长对象,那浅复制节约内存的效果就很突出了。
三.总结浅复制深复制优缺点比较
上面已经对各个场景进行了细说。
总结记忆可以是:浅复制执行效率高,共用内存省内存;深复制隔离影响强。
所以写代码时你是想共用还是想隔离就要思考清楚。
四.深复制的实现方法
实现时要注意3个问题
1.对 基本类型 函数类型 引用类型的判断
2.要写一个深度递归,以及注意采用尾递归来优化递归性能
3.要解决循环引用问题
下面是一个具体实现
//整体想法 对基础类型 函数类型 引用类型分情况处理,用weakMap处理循环引用的情况
const deepClone = (obj, distObj = {}, weakMap = new WeakMap()) => {
if (!obj || typeof obj !== "object") {
return distObj;
}
if (!weakMap.has(obj)) {
weakMap.set(obj, distObj);
}
Object.keys(obj).forEach((key) => {
const keyValue = obj[key];
//函数的情况
if (typeof keyValue === "function") {
distObj[key] = keyValue;
//对象情况
} else if (keyValue && typeof keyValue === "object") {
//说明还没有发生循环引用
if (!weakMap.has(keyValue)) {
//复制引用对象
const dt = { ...keyValue };
distObj[key] = dt;
//把值存入weakMap
weakMap.set(keyValue, dt);
deepClone(dt, dt, weakMap);
} else {
//说明发生了循环引用
distObj[key] = weakMap.get(keyValue);
}
} else {
//基础类型情况
distObj[key] = keyValue;
}
});
return distObj;
};
//测试用例 构造循环引用
const ddd = { aa: 44, c: 3, fun: () => 0 };
const ttt = { d: 4, g: { d: ddd } };
ddd.t = ttt;
console.log("克隆结果", deepClone(ttt));
浅复制实现的两个方法(上面的代码能够实现浅复制)。
const obj={a:1,c:{d:2}};
const obj2={...obj};/// es6 对象扩展... 简单实现浅复制
const obj3=Object.assign({},obj);//Object.assign 这个api实现浅复制
obj.c===obj2.c===obj3.c;//三个都相对
五.常用的json实现深复制注意事项
1.underfine值不能复制
2.函数不能复制
3.循环引用问题无法解决,会报异常,需要做错误处理
具体可以看看别人的 //www.greatytc.com/p/52db1d0c1780