什么是深度克隆,通俗的说:就是在给你一个对象的前提下,你创建一个和这个对象一模一样的对象,所有属性和值都相等,并且修改新对象的属性不会影响旧的对象。 网上常见的两种比较简洁的方式: 方式1:这个方式像极了我们的原型继承,所以我暂时叫他原型式克隆 方式2:这个方式采用的是构造函数实现的,我暂时叫他构造函数式克隆 接下来我们一步步分析他们的优劣: 方式2我觉得问题比较多,先分析它,我们写一个测试: 似乎没什么问题,稍微修改一下 那么运行上面的结果将是: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) 修改之后代码如下: 这个修改就完美了吗?我们再修改一下程序 可以看到,我们修改克隆之后的对象的属性值 被克隆的对象也被修改了,这显然不是深度克隆的宗旨,深度克隆,即使引用类型的属性,也要重新在内存中申请,不能复制引用。问题出在哪里呢? 对就是那句 “这句有很大问题”: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判断属性的是继承属性还是常规属性 |
js 克隆
最新推荐文章于 2020-07-12 22:45:09 发布