关于对象深拷贝和数组去重的问题...
时间:2010-09-24 来源:岑安
中秋最后一天假期,还是憋屈在家里,没事可做,发发关于两个看似很基础却又很有意义的两个问题的一点感想,如题...
一.对象深拷贝:
对应的浅拷贝,对象是通过地址指向来获得引用的,所以单纯的用一个新对象指向源对象就是浅拷贝,对新对象的操作同样会影响的源对象。好比小明有个U盘,里面装有一些资料,一天,小红也需要这个U盘里的资料,于是拿过来改了里面的资料,结果小明再用时,里面资料就变了,因为两个人用的其实是同一个u盘嘛... 如下:
var obj = {a:'a'}; var obj1 = obj; obj.a = 'a1'; alert(obj.a); alert(obj1.a);
运行
那么怎么样才能资料各自私有而不影响对方呢,显而易见嘛,让小红也去买个u盘,并把资料也拷一份到她的u盘里,各用个的就行了。于是:
function clone(myObj) { if(typeof(myObj) != 'object' || myObj == null) return myObj; var myNewObj = new Object(); for(var i in myObj) myNewObj[i] = clone(myObj[i]);//递归调用,当对象中又有对象时 return myNewObj; } var obj = {a:'a'}; var obj1 = obj; var obj2 = clone(obj); obj.a = 'a1'; alert(obj.a); alert(obj1.a); alert(obj2.a);
运行
至此,区别也很明显了,obj1浅拷贝由于是访的同一个对象,故改变源对象obj,新对象obj1也随之改变。而深拷贝obj2是新建了一个对象实例,与源对象obj互不干扰。
二.关于数组去重
网上很流行一种解法,如下:
Array.prototype.filter = function(){ for(var i=0,temp={},result=[],ci;ci=this[i++];){ if(temp[ci])continue; temp[ci]=1; result.push(ci); } return result; } var aa=['1','2','3','1','2']; alert(aa.filter());
运行
它其实是用了对象的特性,把每个数组元素当做一个对象的属性来判断是否存在,这样做有个好处是不用二次遍历数组。然而,乍一看结果没问题,可是一旦数组里有了number,布尔值,对象等作为元素的时候,就会发现问题了。
原因在于:对象则要求其属性值必须为字符串,如果提供给对象的属性不是字符串,那么则会自动调用 toString 方法转化为字符串形式,于是,严格来讲,这种方法只能在数组元素全为字符串时才成立
我们再看看jquery的源码是怎么实现数组去重的方法的:
unique: function( array ) { var ret = [], done = {}; try { for ( var i = 0, length = array.length; i < length; i++ ) { var id = jQuery.data( array[ i ] ); if ( !done[ id ] ) { done[ id ] = true; ret.push( array[ i ] ); } } } catch( e ) { ret = array; } return ret; },
乍一看,其实思路跟我上面说的方法是一样的,可是别忘了很重要的一点,var id = jQuery.data( array[ i ] );这一句,其实是把每个数组元素都转换成了节点数组再做的判断。
所以,如果把安全性放在首位,效率次之的话,数组去重还是应该老老实实二次遍历数组,如下:
function undulpicate(array){ for(var i=0;i<array.length;i++) { for(var j=i+1;j<array.length;j++) { //注意 === if(array[i]===array[j]) { array.splice(j,1); j--; } } } return array; }
当然,如果能确定数组元素类型,大可不必这样做,毕竟二次遍历在数据量大的时候是个不小的消耗。。
此外,貌似hash查找可以减少一次遍历...具体还没想过...唉,今天就到此位置吧