谈谈JavaScript深浅拷贝
浅拷贝
1 function shallowCopy(source){ 2 3 var newObj = {}; 4 5 for(var attr in source){ 6 newObj[attr] = source[attr]; 7 } 8 9 return newObj; 10 11 }
一般简单的对象用浅拷贝就行了,深拷贝是对付哪种对象中还有对象的类型。
深拷贝
1 function deepCopy(source){ 2 var newObj = {}; 3 4 if(typeof source != 'object'){ 5 //console.trace(); 6 return source; 7 } 8 9 for(var attr in source){ 10 11 newObj[attr] = deepCopy(source[attr]); 12 } 13 //console.trace(); 14 //console.log(newObj); 15 return newObj; 16 17 18 } 19 var obj1 = { 20 a : { b : 10 } 21 }; 22 var obj2 = deepCopy(obj1);//第一层循环为 obj2=deepCopy(obj1)----->newObj[a]=deepCopy(obj1[a])=deepCopy({b:10})------->newObj[a]= 23 //第二层循环为 newObj[b]=deepCopy({b:10}[b])=deepCopy(10)=10------->newObj[b]=10------->return newObj={b:10} 24 //newObj[a]={b:10}------>return newObj=a:{b:10} 25 26 // obj2.a.b = 20; 27 alert(obj2.a.b); //10 28 alert(obj1.a.b); //10 29 console.log(obj1,obj2)
主要是用了递归的思想:
obj2=deepCopy(obj1)----->调用deepCopy函数,传入的参数是obj1.第一个if判断是否是对象,如果是则执行下面的for/in循环,如果不是则返回obj。第一个obj1显然是对象,所以执行for/in循环。得到的结果是:
- newObj[a]=deepCopy(obj1[a])=deepCopy({b:10})----->再次调用deepCopy函数,传入的参数是对象{b:10},执行for/in循环。得到的结果是:
- newObj[b]=deepCopy({b:10}[b])=deepCopy(10)-------->再调用deepCopy函数,传入的参数是{10},不是对象,返回obj,也就是10,得到的结果是:
- newObj[b]=deepCopy({b:10}[b])=deepCopy(10)=10.
至此,整个“递”的过程就完成了。下面是“归”的过程。也就是一个相反的过程
- deepCopy(10)=10. 代入上面的第二步,执行for/in循环得到newObj[b]=10,然后return newObj={b:10}
- 代入上面的第一步,执行for/in循环得到newObj[a]=deepCopy(obj1[a])=deepCopy({b:10}) deepCopy函数执行返回就是第一步的return newObj={b:10},所以newObj[a]={b:10},return newObj={a:{b:10}}
在return newObj;上添加console.log(newObj);输出结果如下:
可以看到的确是return了两次。第一次为{b:10},第二次为{a:{b:10}}.
另一种实现是使用parse和stringify:
1 var a = { 2 name : { age : 100 } 3 }; 4 5 var str = JSON.stringify(a); 6 7 var b = JSON.parse(str); 8 9 b.name.age = 200; 10 11 alert( a.name.age ); //100
它能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,即那些能够被 json 直接表示的数据结构。
我们经常会遇到 手上有数组对象A 要拷贝一个数组对象B,同时修改B不会改变A中元素的值的需求
let o1 = [{name:1,value:1},{name:2,value:2}]; let o2 = JSON.parse(JSON.stringify(o1)); //随便怎么改o2都不会对o1产生任何影响 let o3 = o2.map(item=>{ delete item.name return { ...item, sex:item.value * item.value } })
还可以获得新数组对象o3
数组的话就很简单了 有slice 和 concat方法
let a1 = [1,2,3] let a2 = a1.concat() let a3 = a1.slice() a2[0] = 2 a3[0] = 3 console.log(a1) console.log(a2) console.log(a3) // a1 [1,2,3] // a2 [2,2,3] // a3 [3,2,3]