js 克隆


什么是深度克隆,通俗的说:就是在给你一个对象的前提下,你创建一个和这个对象一模一样的对象,所有属性和值都相等,并且修改新对象的属性不会影响旧的对象。

网上常见的两种比较简洁的方式:
方式1:这个方式像极了我们的原型继承,所以我暂时叫他原型式克隆
  1. function clone1(obj1)
  2. {
  3.    function F() {} ;
  4.    F.prototype = obj1 ;
  5.    var f = new F() ;
  6.    for(var key in obj1)
  7.    {
  8.      if(typeof obj1[key] =="object")
  9.      {
  10.          f[key] = clone1(obj1[key])
  11.      }
  12.   }
  13. return f ;
  14. }
复制代码
方式2:这个方式采用的是构造函数实现的,我暂时叫他构造函数式克隆
  1. function clone2(obj1)
  2. {
  3.      var o;
  4.      if(obj1.constructor==Object)
  5.       { 
  6.         o = new obj1.constructor() ;
  7.       }
  8.     else
  9.      {
  10.       o = new obj1.constructor(obj1.valueOf())
  11.      }
  12.     for(var key in obj1)
  13.     {
  14.       if(obj1[key]!=o[key])//这句有很大的问题
  15.        {
  16.           if(typeof obj1[key] =="object")
  17.             o[key] = clone2(obj1[key]) ;
  18.          else
  19.             o[key] = obj1[key] ;
  20.       }
  21. }
  22. o.toString = obj1.toString;
  23.   o.valueOf = obj1.valueOf;
  24. return o;
  25. }
复制代码
接下来我们一步步分析他们的优劣:
方式2我觉得问题比较多,先分析它,我们写一个测试:
  1. function Obj1(){
  2.    this.a = 1 ;
  3.    this.b = 2 ;
  4. }
  5. var o = new Obj1() ;
  6. alert(o.a) ;//打印1
  7. alert(o.b) ;//打印2

  8. var o1 = clone2(o) ;
  9. alert(o1.a) ;//打印1
  10. alert(o1.b) ;//打印2
  11. o1.a = 3 ;
  12. alert(o1.a) ;//打印3
  13. alert(o.a) ;//打印1
复制代码
似乎没什么问题,稍微修改一下
  1. function Obj1(){
  2. this.a = 1 ;
  3. this.b = null ;
  4. }
复制代码
那么运行上面的结果将是:Cannot read property 'constructor' of null
问题在哪里呢?null这个值,既不是引用类型,也不是基本数据类型,但是 typoef null == "object"这个是成立的。
然而null却不是对象,所以没有constructor这个属性,程序报错!!

只需要把clone2的 if(typeof obj1[key] =="object") 改为 if(typeof obj1[key] =="object"&&obj1[key] !=null) 
修改之后代码如下:
  1. function clone2(obj1)
  2. {
  3.      var o;
  4.      if(obj1.constructor==Object)
  5.       { 
  6.         o = new obj1.constructor() ;
  7.       }
  8.     else
  9.      {
  10.       o = new obj1.constructor(obj1.valueOf())
  11.      }
  12.     for(var key in obj1)
  13.     {
  14.       if(obj1[key]!=o[key])
  15.        {
  16.           if(typeof obj1[key] =="object"&&obj1[key]!=null)
  17.             o[key] = clone2(obj1[key]) ;
  18.          else
  19.             o[key] = obj1[key] ;
  20.       }
  21.    }
  22. return o ; 
  23. }
复制代码
这个修改就完美了吗?我们再修改一下程序
  1. function Obj1(){
  2. this.a = 1 ;
  3. this.b = 2 ;
  4. }
  5. Obj1.prototype.arr = [1,2,3,4] ;
  6. var o = new Obj1() ;
  7. alert(o.arr[3]) ;//打印4

  8. var o1 = clone2(o) ;
  9. alert(o1.arr[3]) ;//打印4
  10. o1.arr[3] = 8 ;
  11. alert(o1.arr[3]) ;//打印8
  12. alert(o.arr[3]) ;//打印8
复制代码
可以看到,我们修改克隆之后的对象的属性值 被克隆的对象也被修改了,这显然不是深度克隆的宗旨,深度克隆,即使引用类型的属性,也要重新在内存中申请,不能复制引用。问题出在哪里呢?

对就是那句 “这句有很大问题”:if(obj1[key]!=o[key]) 
首先我们要理解原型,原型是对象共享数据的地方,原型中的属性被称为继承属性,非原型中的属性是常规属性,继承属性中的引用类型是被所有对象共享的。
通过o = new obj1.constructor(obj1.valueOf()) ,那么o和obj1拥有同样的原型,都继承了arr属性,由于arr是一个引用的类型,所以
obj1.arr==o.arr是成立的。所以执行到这里,clone2跳过了克隆arr,直接复制arr。

所以我觉得去掉这个if(obj1[key]!=o[key])  判断比较好。如果觉得这样每个属性都遍历一遍有点麻烦的话。
可以把if(obj1[key]!=o[key])  改为if(!obj1.hasOwnProperty(key)),就是说,如果是常规属性,那么就不必克隆了。
这里有个知识点:常规属性,通过new创建的对象,都是对象独立拥有的,包括引用数据类型。

问题到这里是否真的完美了呢?我认为有一点小瑕疵
引用类型包括函数,所以函数的克隆clone2无法办到。我也没有找到相应的解决办法?不知道可以用 Function不,哪位大神知道?

但是由于函数属性是只读的,没有必要修改,所以深度克隆不克隆函数属性,问题也不大。


文字真多,下面再看看clone1吧,这个方法似乎没有clone2那么多漏洞
但是仔细观察就会发现。克隆之后,对象属性全部变成了原型里面的属性。似乎从结构上,不同于原来的对象了。也不能通过
hasOwnProperty判断属性的是继承属性还是常规属性
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值