一篇详解JS的深拷贝和浅拷贝By。第二类是好书生,希望在JS方面有扎实的基础,方便以后去面试官show操作。对于第一类读者,只需要一行黑科技代码就可以实现深拷贝varcopyObj={name:'ziwei',arr:[1,2,3]}vartargetObj=JSON.parse(JSON.stringify(copyObj))此时copyObj.arr!==targetObj.arr已经实现了深拷贝。复制copyObj对象原型链上的属性和方法当然,在清楚了解它们的缺点后,如果它们的缺点对你的业务需求没有影响,你就可以放心使用,一行native代码就搞定.目前我在开发业务场景的时候,上面这两个缺点大部分真的可以忽略不计。对象中往往没有需要深拷贝的函数,也不需要拷贝其原型链的属性。对于第二类读者,我会尽可能全面的讲解JS中对象的复制。要清楚地解释复制,您需要一些预备知识。你需要的前置知识:了解JS中的引用类型和值类型区别在于你知道Obj只存储引用,对原型链有基本的了解。关于对象拷贝的一切:1.什么是深拷贝和浅拷贝2.深拷贝和浅拷贝在业务中最常见的应用场景3.深拷贝和浅拷贝的实现4.总结和建议1.什么是深拷贝而浅拷贝仍然是浅拷贝。比如下面这个对象varobj1={name:'ziwei',arr:[1,2,3]}因为,如果是这样的{name:'ziwei'},就不需要对嵌套对象进行区分浅色和深色副本。只有存在嵌套对象时,才会有深拷贝和浅拷贝的区别。shallowcopy是什么样子的(具体实现暂时先不关心,因为下面会讲到)调用shallowCopy()后,obj2复制了obj1的所有属性。但是obj2.arr和obj1.arr是不同的引用,指向同一个内存空间varobj2=shallowCopy(obj1,{})console.log(obj1!==obj2)//true不管是哪种拷贝,obj1和obj2mustBothare2differentobjects(differentmemoryspace)console.log(obj2.arr===obj1.arr)//true它们2个对象中arr的引用指向[同一个]内存空间所以,2个obj后复制,虽然它们的属性相同,确实是不同的对象,但是它们内部的objs都指向同一个内存空间。这就是我们所说的浅拷贝和深拷贝(我们暂时不关心如何实现,因为下面会讲到)调用deepCopy()后,obj2复制了obj1的所有属性,而obj2.arr和obj1.arr指向不同的内存空间,两个obj2除了复制相同的属性外,没有其他关联。varobj2=deepCopy(obj1,{})console.log(obj1!==obj2)//true不管是哪种拷贝,obj1和obj2必须是2个不同的对象(不同的内存空间)console.log(obj2.arr===obj1.arr)//false他们两个对象中arr的引用指向[不同的]内存空间。因此,这两个obj复制后,除了复制2个对象的相同属性外,没有其他关联,我们称这种深复制2。深复制在业务中最常见的应用场景,举个栗子。业务需求是:一个表格展示商品的各种信息,点击【同意】,弹出对话框调整商品数量。在这种业务需求下,我们将使用对象的深拷贝。因为【商品形态】的属性和【调整商品形态】的属性几乎一样,所以我们需要复制它们。下面的伪代码和图片展示了使用浅拷贝的问题。这样得到的adjustedTableArr和tableArr中,内部对象是一样的,所以出现了图中的红线。当我们修改[AdjustProductTable]中的商品数量时,[producttable]也随之改变,这不是我们想要的//表对象的数据结构vartableArr=[{goods_name:'Long-袖背夹',code:'M216C239E0864',num:'2'},{goods_name:'长袖背夹',code:'M216C240B0170',num:'3'},{goods_name:'塑料短裤',code:'M216D241C04106',num:'3'},]varadjustTableArr=[]//用于调整表的数组for(varkeyintableArr){//浅拷贝adjustTableArr[key]=tableArr[key]}In其实我们希望这两个表的数据是完全独立的,互不干扰。调整确认后才会刷新商品数量。这种情况下,我们就可以使用深拷贝这个单行黑科技了varadjustTableArr=JSON.parse(JSON.stringify(tableArr))还记得它的缺陷吗?无法复制属性。对这里的业务没有影响,可以很容易的进行深拷贝。3、深拷贝和浅拷贝的实现方法其实JQ已经有了$.extend()函数,实现的就是深拷贝和浅拷贝的功能。有兴趣的朋友也可以看看源码。浅拷贝浅拷贝比较简单,就是用forinloopassignmentfunctionshallowCopy(source,target={}){varkey;for(keyinsource){if(source.hasOwnProperty(key)){//表示__proto__以上属性,我不复制target[key]=source[key];}}返回目标;}深拷贝实现深拷贝,就是遍历复制的对象,判断对象中每一项的数据类型,如果不是对象,如果是对象类型,直接赋值。如果是对象类型,再次调用deepCopy递归赋值。函数deepCopy(source,target={}){varkey;for(keyinsource){if(source.hasOwnProperty(key)){//表示__proto__之上的属性,我不复制if(typeof(source[key])==="object"){//如果这样item是object类型,递归调用deepCopytarget[key]=Array.isArray(source[key])?[]:{};deepCopy(源[键],目标[键]);}else{//如果不是对象类型,直接赋值复制target[key]=source[key];}}}返回目标;使用了source.hasOwnProperty(key),意思是判断这个item是否是自己的property,是则复制,不是则不复制。也就是说,我不复制__proto__上面的属性。其实你可以根据自己的业务需要决定添加和这个条件(JQ的$.extend()甚至会复制__proto__上的属性,但是是直接复制到对象中,而不是放在__proto__之前)4.总结和建议虽然你可能经常使用框架提供的API来实现深拷贝。分享这篇文章的目的更多的是想用一篇文章来梳理一下深拷贝和浅拷贝的含义,递归实现的思路,如果用到了JSON.parse()这个黑科技,你一定知道它的优点写这样的缺点。5.修正上面deepCopy方法的漏洞,一开始没有考虑source是数组。以下是修改后的版本functiondeepCopy(source){lettarget=Array.isArray(source)?[]:{}for(varkinsource){if(typeofsource[k]==='object'){target[k]=deepCopy(source[k])}else{target[k]=source[k]}}返回目标}
