在说深拷贝和浅拷贝之前,我们先来看两个简单的案例://Case1varnum1=1,num2=num1;console.log(num1)//1console.log(num2)//1num2=2;//修改num2console.log(num1)//1console.log(num2)//2//Case2varobj1={x:1,y:2},obj2=obj1;console.log(obj1)//{x:1,y:2}console.log(obj2)//{x:1,y:2}obj2.x=2;//修改obj2.xconsole。log(obj1)//{x:2,y:2}console.log(obj2)//{x:2,y:2}按照常规思路,obj1应该和num1一样,不会因为到另一个值并改变,但是这里obj1随着obj2的改变而改变了。同样的变量,为什么表现不一样?这是介绍JS中基本类型和引用类型的概念。原始类型和引用类型ECMAScript变量可能包含两种不同数据类型的值:原始类型值和引用类型值。原始值是指存储在栈内存中的那些简单的数据段,即此类值在内存中完全存储在一个位置。引用类型的值是指那些保存在堆内存中的对象,也就是说变量中保存的其实只是一个指针,而这个指针指向内存中的另一个位置,也就是保存对象的地方。比如基本类型和引用类型在赋值上的区别,可以理解为“连锁店”和“单店”:基本类型赋值等于在新地方安装连锁店,新开分店,新开的store与其他老店不相关,独立经营;而引用类型赋值相当于一个店铺有两把钥匙,交给两个老板同时管理,两个老板的??行为都可能影响店铺的经营。上面很清楚的介绍了基本类型和引用类型的定义和区别。目前基本类型有:Boolean、Null、Undefined、Number、String、Symbol,引用类型有:Object、Array、Function。之所以说“目前”是因为Symbol是在ES6才发布的,以后可能会有新的类型出来。回到前面的案例,案例1中的值是基本类型,案例2中的值是引用类型。案例2中的赋值是典型的浅拷贝,深拷贝和浅拷贝的概念只存在于引用类型中。深拷贝和浅拷贝知道了深拷贝和浅拷贝的道理,那么深拷贝怎么实现呢?我们先看看是否支持Array和Object自身的方法:Arrayvararr1=[1,2],arr2=arr1.slice();console.log(arr1);//[1,2]console.log(arr2);//[1,2]arr2[0]=3;//修改arr2console.log(arr1);//[1,2]console.log(arr2);//此时[3,2],arr2的修改并没有影响arr1。看来深拷贝的实现并没有那么难。我们把arr1换成二维数组看看:vararr1=[1,2,[3,4]],arr2=arr1.slice();console.log(arr1);//[1,2,[3],4]]console.log(arr2);//[1,2,[3,4]]arr2[2][1]=5;console.log(arr1);//[1,2,[3],5]]console.log(arr2);//[1,2,[3,5]]哎,arr2又改arr1了,好像slice()只能实现一维的深拷贝大批。也有相同的特性:concat、Array.from()。Object1,Object.assign()varobj1={x:1,y:2},obj2=Object.assign({},obj1);console.log(obj1)//{x:1,y:2}控制台。log(obj2)//{x:1,y:2}obj2.x=2;//修改obj2.xconsole.log(obj1)//{x:1,y:2}console.log(obj2)//{x:2,y:2}varobj1={x:1,y:{m:1}};varobj2=Object.assign({},obj1);console.log(obj1)//{x:1,y:{m:1}}console.log(obj2)//{x:1,y:{m:1}}obj2.y.m=2;//修改obj2.y.mconsole.log(obj1)//{x:1,y:{m:2}}console.log(obj2)//{x:2,y:{m:2}}经测试,Object.assign()只能实现一维对象的深拷贝。2.JSON.parse(JSON.stringify(obj))varobj1={x:1,y:{m:1}};varobj2=JSON.parse(JSON.stringify(obj1));console.log(obj1)//{x:1,y:{m:1}}console.log(obj2)//{x:1,y:{m:1}}obj2.y.m=2;//修改obj2.y.mconsole。log(obj1)//{x:1,y:{m:1}}console.log(obj2)//{x:2,y:{m:2}}JSON.parse(JSON.stringify(obj))看起来很不错,但是MDN文档的描述有一句话说的很清楚:undefined,arbitraryfunctions,andsymbolvalueswillbeignoredduringserialization(当它们出现在的属性值中时非数组对象)或转换为null(当存在于数组中时)。让我们再次转换obj1:varobj1={x:1,y:undefined,z:functionadd(z1,z2){returnz1+z2},a:Symbol("foo")};varobj2=JSON.parse(JSON.stringify(obj1));console.log(obj1)//{x:1,y:undefined,z:?,a:Symbol(foo)}console.log(JSON.stringify(obj1));//{"x":1}console.log(obj2)//{x:1}发现在obj1序列化为JSON.stringify()的过程中,y,z,a都被忽略了,这也验证了MDN文档的描述.在这种情况下,JSON.parse(JSON.stringify(obj))的使用也受到限制。包含undefined、function、symbol值的对象无法深度复制,但是JSON.parse(JSON.stringify(obj))简单粗暴,已经满足90%的使用场景。经过验证,发现JS提供的自有方法并不能完全解决Array和Object的深拷贝问题。只能用大杀器:递归函数deepCopy(obj){//新建对象letresult={}letkeys=Object.keys(obj),key=null,temp=null;for(leti=0;i
