来自:mjsws > 馆藏分类
配色: 字号:
深入了解JavaScript中的浅拷贝和深拷贝
2018-04-22 | 阅:  转:  |  分享 
  
深入了解JavaScript中的浅拷贝和深拷贝这篇文章介绍的内容是关于深入了解JavaScript中的浅拷贝和深拷贝,有着一定的参考价值,
现在分享给大家,有需要的朋友可以参考一下在JS中有一些基本类型像是Number、String、Boolean,而对象就是像这样
的东西{name:''Larry'',skill:''Node.js''},对象跟基本类型最大的不同就在于他们的传值方式。基本类
型是按值传递,像是这样:在修改a时并不会改到b12345var?a=25;var?b=a;b=18;console.l
og(a);//25console.log(b);//18但对象就不同,对象传的是按引用传值:1234567var?obj1=
{a:10,b:20,c:30};var?obj2=obj1;obj2.b=100;console.log(
obj1);//{a:10,b:100,c:30}<--b被改到了console.log(obj2);//
{a:10,b:100,c:30}复制一份obj1叫做obj2,然后把obj2.b改成100,但却不小心改到obj1
.b,因为他们根本是同一个对象,这就是所谓的浅拷贝。要避免这样的错误发生就要写成这样:1234567var?obj1={a:
10,b:20,c:30};var?obj2={a:obj1.a,b:obj1.b,c:obj1.c
};obj2.b=100;console.log(obj1);//{a:10,b:20,c:30}<--b
沒被改到console.log(obj2);//{a:10,b:100,c:30}这样就是深拷贝,不会改到原本的
obj1。浅拷贝(ShallowCopy)VS深拷贝(DeepCopy)浅拷贝只复制指向某个对象的指针,而不复制对象本身,
新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。浅拷贝的实现
方式也就是简单地复制而已1、简单地复制语句12345678910111213141516171819202122232425262
728?function?simpleClone(initalObj
){?var?obj={};?for?(?var?iininitalObj){?obj[i]=initalObj[
i];?}?return?obj;?}?var?obj={?a:?"hello",?b:{?a:?"world",?b:21
?},?c:["Bob",?"Tom",?"Jenny"],?d:function(){?alert("helloworld"
);?}?}?var?cloneObj=simpleClone(obj);?console.log(cloneObj.b);?
console.log(cloneObj.c);?console.log(cloneObj.d);??cloneObj.b.a=
?"changed";?cloneObj.c=[1,2,3];?cloneObj.d=?function(){ale
rt("changed");};?console.log(obj.b);?console.log(obj.c);?console
.log(obj.d);?结果为:2、Object.assign()Object.assign是ES6的新函数
。Object.assign()?方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是?Object.
assign()?进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。1Object.assign(target,...s
ources)参数:target:目标对象。sources:任意多个源对象。返回值:目标对象会被返回。12345var?obj=
{a:{a:?"hello",b:21}};var?initalObj=Object.assign({},obj
);?initalObj.a.a=?"changed";console.log(obj.a.a);?//"changed"兼容
性:需要注意的是:1Object.assign()可以处理一层的深度拷贝,如下:1234567var?obj1={a:10
,b:20,c:30};var?obj2=Object.assign({},obj1);obj2.b=100;
console.log(obj1);//{a:10,b:20,c:30}<--沒被改到console.log(
obj2);//{a:10,b:100,c:30}深拷贝的实现方式要完全复制又不能修改到原对象,这时候就要用De
epCopy,这里会介绍几种DeepCopy的方式。1、手动复制把一个对象的属性复制给另一个对象的属性1234567var?
obj1={a:10,b:20,c:30};var?obj2={a:obj1.a,b:obj1.b,
c:obj1.c};obj2.b=100;console.log(obj1);//{a:10,b:20,c:
30}<--沒被改到console.log(obj2);//{a:10,b:100,c:30}但这样很麻烦
,要一个一个自己复制;而且这样的本质也不能算是DeepCopy,因为对象里面也可能回事对象,如像下面这个状况:12345678
91011var?obj1={body:{a:10}};var?obj2={body:obj1.body
};obj2.body.a=20;console.log(www.267774.comobj1);//{body:{
a:20}}<--被改到了console.log(obj2);//{body:{a:20}}console
.log(obj1===obj2);//falseconsole.log(obj1.body===obj2.body);
//true虽然obj1跟obj2是不同对象,但他们会共享同一个obj1.body,所以修改obj2.body.a时也会修改到旧
的。2、对象只有一层的话可以使用上面的:Object.assign()函数Object.assign({},obj1)的意思是先
建立一个空对象{},接着把obj1中所有的属性复制过去,所以obj2会长得跟obj1一样,这时候再修改obj2.b也不会影响obj
1。因为Object.assign跟我们手动复制的效果相同,所以一样只能处理深度只有一层的对象,没办法做到真正的DeepCop
y。不过如果要复制的对象只有一层的话可以考虑使用它。3、转成JSON再转回来用JSON.stringify把对象转成字符串,再
用JSON.parse把字符串转成新的对象。1234567891011var?obj1={body:{a:10}}
;var?obj2=JSON.parse(JSON.stringify(obj1));obj2.body.a=20;con
sole.log(obj1);//{body:{a:10}}<--沒被改到console.log(obj2);/
/{body:{a:20}}console.log(obj1===obj2);//falseconsole.l
og(obj1.body===obj2.body);//false这样做是真正的DeepCopy,这种方法简单易用。但是这
种方法也有不少坏处,譬如它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会
变成Object。这种方法能正确处理的对象只有?Number,String,Boolean,Array,扁平对象,即那些能
够被json直接表示的数据结构。RegExp对象是无法通过这种方式深拷贝。也就是说,只有可以转成JSON格式的对象才可以这样用
,像function没办法转成JSON。123456var?obj1={fun:?function(){console.l
og(123)}};var?obj2=JSON.parse(JSON.stringify(obj1));console.l
og(typeofobj1.fun);//''function''console.log(typeofobj2.fun);//
''undefined''<--没复制要复制的function会直接消失,所以这个方法只能用在单纯只有数据的对象。4、递归拷贝12
34567891011121314function?deepClone(initalObj,finalObj){?var?ob
j=finalObj||{};?for?(var?iininitalObj){?if?(typeofinitalO
bj[i]===?''object''){?obj[i]=(initalObj[i].constructor===Arra
y)?[]:{};?arguments.callee(initalObj[i],obj[i]);?}?else?{?ob
j[i]=initalObj[i];?}?}?return?obj;}var?str={};var?obj={a:
{a:?"hello",b:21}};deepClone(obj,str);console.log(str.a);上述代码
确实可以实现深拷贝。但是当遇到两个互相引用的对象,会出现死循环的情况。为了避免相互引用的对象导致死循环的情况,则应该在遍历的时候判
断是否相互引用对象,如果是则退出循环。改进版代码如下:123456789101112131415161718function?de
epClone(initalObj,finalObj){?var?obj=finalObj||{};?for?(var
?iininitalObj){?var?prop=initalObj[i];?//避免相互引用对象导致死循环,如ini
talObj.a=initalObj的情况?if(prop===obj){?continue;?}?if?(typeof
prop===?''object''){?obj[i]=(prop.constructor===Array)?[]
:{};?arguments.callee(prop,obj[i]);?}?else?{?obj[i]=prop;?}?}
?return?obj;}var?str={};var?obj={a:{a:?"hello",b:21}};de
epClone(obj,str);console.log(str.a);5、使用Object.create()方法直接使用var
newObj=Object.create(oldObj),可以达到深拷贝的效果。123456789101112131415f
unction?deepClone(initalObj,finalObj){?var?obj=finalObj||{}
;?for?(var?iininitalObj){?var?prop=initalObj[i];?//避免相互引用对象
导致死循环,如initalObj.a=initalObj的情况?if(prop===obj){?continue;?}?
if?(typeofprop===?''object''){?obj[i]=(prop.constructor===Ar
ray)?[]:Object.create(prop);?}?else?{?obj[i]=prop;?}?}?retu
rn?obj;}6、jqueryjquery有提供一个$.extend可以用来做DeepCopy。123456var?$=
?require(''jquery'');var?obj1={?a:1,?b:{f:{g:1}},?c:[1,2,3]};var?obj2=$.extend(true,{www.jiekeqipai.net},obj1);console.log(obj1.b.f===obj2.b.f);//false7、lodash另外一个很热门的函数库lodash,也有提供_.cloneDeep用来做DeepCopy。123456var?_=?require(''lodash'');var?obj1={?a:1,?b:{f:{g:1}},?c:[1,2,3]};var?obj2=_.cloneDeep(obj1);console.log(obj1.b.f===obj2.b.f);//false这个性能还不错,使用起来也很简单。
献花(0)
+1
(本文系mjsws首藏)