详解JavaScript对象的深浅复制
前言
从层次上来看,对象的复制可以简单地分为浅复制和深复制,顾名思义,浅复制是指只复制一层对象的属性,不会复制对象中的对象的属性,对象的深复制会复制对象中层层嵌套的对象的属性。
在复制对象时,除了要复制对象的属性外,还要兼顾到是否保留了对象的constructor属性,是否对每一种数据类型(javascript常见的数据类型有string,number,boolean,data,regexp,array,funtion,object)都实现正确的复制。项目中,我们可以根据实际情况,决定需要实现什么样程度的复制。
本文是我在复制对象方面的一些心得总结,由浅复制到深复制,由只复制简单属性到复制function,regexp等复杂属性,层层递进。如有陈述不当之处,烦请指出,不胜感激。
正文
浅复制
浅复制只会依次复制对象的每一个属性,不会对这些属性进行递归复制。下面是一个简单的浅复制实现。
//对象浅复制 function shadowcopy(obj){ if(typeof obj !== 'object') return obj; for(var prop in obj){ if(obj.hasownproperty(prop)){ newobj[prop] = obj[prop]; } } return newobj; }
仔细观察,不难发现上述方法的缺陷:
1.不能正确实现数组的浅复制
2.复制操作丢失了对象的constructor属性
好,我们现在已经发现了问题所在,只需针对性地解决,一个还算完美的浅复制对象的方法就诞生了!
//对象浅复制 function shadowcopy(obj){ if(typeof obj !== 'object') return ; var newobj; //保留对象的constructor属性 if(obj.constructor === array){ newobj = []; } else { newobj = {}; newobj.constructor = obj.constructor; } for(var prop in obj){ if(obj.hasownproperty(prop)){ newobj[prop] = obj[prop]; } } return newobj; }
浏览器中测试一下:
var arr1 = [0,1,2]; console.log(arr1); console.log(shadowcopy(arr1)); var arr2 = [0,1,2,[3,4,5]], arr2copy = shadowcopy(arr2); console.log(arr2); console.log(arr2copy); arr2copy[3][0] = 6; console.log(arr2[3][0]); //6
good! 可以正确实现数组复制和并且保留constructor了,但细心的你一定发现了,浅复制后的对象的 arr2copy[3] 和 arr2[3] 指向的是一个对象,改变其中一个,同时也会改变另一个。我们想要实现的是 复制,但这并不是复制呀!
这是浅复制的一个弊端所在,接下让我们看看深复制是怎样解决这个问题的。
深复制
深复制需要层层递归,复制对象的所有属性,包括对象属性的属性的属性....(晕~)
如果只是需要简单地复制对象的属性,而不用考虑它的constructor,也不用考虑函数,正则,data等特殊数据类型,那这里有一个深复制的小trick,两行代码即可:
function deepcopy(obj){ if(typeof obj !== "object"){ return ;} var str = json.stringify(obj); return json.parse(str); }
大多数情况下,上面的就可以满足要求了,但一些时候,我们需要把函数,正则等特殊数据类型也考虑在内,或者当前环境不支持json时,上面的方法也就不适用了。这时,我们可以通过递归来实现对象的深层复制,如下:
function deepcopy(obj){ if(typeof obj !== "object"){ return ;} var newobj; //保留对象的constructor属性 if(obj.constructor === array){ newobj = []; } else { newobj = {}; newobj.constructor = obj.constructor; } for(var prop in obj){ if(typeof obj[prop] === 'object'){ if(obj[prop].constructor === regexp ||obj[prop].constructor === date){ newobj[prop] = obj[prop]; } else { //递归 newobj[prop] = deepcopy(obj[prop]); } } else { newobj[prop] = obj[prop]; } } return newobj; }
先用上面的例子测试:
棒!可以正确实现多维数组的复制,再看是否能实现函数和正则的复制:
function person(name){ this.name = name; this.age = age; this.search = new regexp(name); this.say = function(){ console.log(this.name + "今年" + this.age + "岁了"); } } var p1 = new person("claiyre",20), p2 = deepcopy(p1); console.log(p1); console.log(p2); p2.age = 22; p1.say(); p2.say();
圆满完成!!
稍加整理,我们就可以得到一个较为通用的js对象复制函数:
function deepcopy(obj){ var newobj = obj.constructor === array ? []:{}; newobj.constructor = obj.constructor; if(typeof obj !== "object"){ return ; } else if(window.json){ //若需要考虑特殊的数据类型,如正则,函数等,需把这个else if去掉即可 newobj = json.parse(json.stringify(obj)); } else { for(var prop in obj){ if(obj[prop].constructor === regexp ||obj[prop].constructor === date){ newobj[prop] = obj[prop]; } else if(typeof obj[prop] === 'object'){ //递归 newobj[prop] = deepcopy(obj[prop]); } else { newobj[prop] = obj[prop]; } } } return newobj; }
结语
面向对象的编程语言,其核心是对象,因此深入了解对象的相关操作,纵向比较异同,对学习过程是极有好处的。
以上所述是小编给大家介绍的javascript对象的深浅复制,希望对大家有所帮助