浅拷贝和深拷贝

2019-08-16  本文已影响0人  Camilia_yang

概念

浅拷贝和深拷贝都可以实现在已有对象的基础上再生一份的作用,但是对象的实例是存储在堆内存中然后通过一个引用值去操作对象,由此拷贝的时候就存在两种情况了:拷贝引用和拷贝实例,这也是浅拷贝和深拷贝的区别所在。

由深复制的定义来看,深复制要求如果源对象存在对象属性,那么需要进行递归复制,从而保证复制的对象与源对象完全隔离。然而还有一种可以说处在浅复制和深复制的粒度之间,也是jQuery的extend方法在deep参数为false时所谓的“浅复制”,这种复制只进行一个层级的复制:即如果源对象中存在对象属性,那么复制的对象上也会引用相同的对象。这不符合深复制的要求,但又比简单的复制引用的复制粒度有了加深。

浅拷贝

浅复制就是简单的引用复制

var src = {
       name:"src"
   }
   //复制一份src对象的应用
   var target = src;
   target.name = "target";
   console.log(src.name);   //输出target

target对象只是src对象的引用值的复制,因此target的改变也会影响src。

深拷贝

  1. Array的slice和concat方法
    Array的slice和concat方法都会返回一个新的数组实例,但是这两个方法对于数组中的对象元素却没有执行深复制,而只是复制了引用了,因此这两个方法并不是真正的深复制
var array = [1,2,3];
var array_shallow = array;
var array_concat = array.concat();
var array_slice = array.slice(0);
console.log(array === array_shallow);   //true
console.log(array === array_slice);     //false
console.log(array === array_concat);    //false

上面的代码表明了concat和slice返回的是新的数组实例。

var array = [1, [1,2,3], {name:"array"}];
var array_concat = array.concat();
var array_slice = array.slice(0);
//改变array_concat中数组元素的值
array_concat[1][0] = 5;
console.log(array[1]);    //[5,2,3]
console.log(array_slice[1]);  //[5,2,3]
//改变array_slice中对象元素的值
array_slice[2].name = "array_slice";
console.log(array[2].name);   //array_slice
console.log(array_concat[2].name); //array_slice

但通过上面的代码可以看出,concat和slice并不是真正的深复制,数组中的对象元素(Object,Array等)只是复制了引用。

  1. JSON对象的parse和stringify方法
    JSON对象parse方法可以将JSON字符串反序列化成JS对象,stringify方法可以将JS对象序列化成JSON字符串,借助这两个方法,也可以实现对象的深复制。
var source = {
    name:"source",
    child:{
        name:"child"
    }
}
var target = JSON.parse(JSON.stringify(source));
//改变target的name属性
target.name = "target";
console.log(source.name);   //source
console.log(target.name);   //target
//改变target的child
target.child.name = "target child";
console.log(source.child.name);  //child
console.log(target.child.name);  //target child

这个方法使用较为简单,可以满足基本的深复制需求,而且能够处理JSON格式能表示的所有数据类型,但是对于正则表达式类型、函数类型等无法进行深复制(而且会直接丢失相应的值),同时如果对象中存在循环引用的情况也无法正确处理

  1. 递归实现
//util作为判断变量具体类型的辅助模块
   var util = (function(){
       var class2type = {};
       ["Null","Undefined","Number","Boolean","String","Object","Function","Array","RegExp","Date"].forEach(function(item){
           class2type["[object "+ item + "]"] = item.toLowerCase();
       })
 
       function isType(obj, type){
           return getType(obj) === type;
       }
       function getType(obj){
           return class2type[Object.prototype.toString.call(obj)] || "object";
       }
       return {
           isType:isType,
           getType:getType
       }
   })();
 
   function copy(obj,deep){
        //如果obj不是对象,那么直接返回值就可以了
       if(obj === null || typeof obj !== "object"){
           return obj;
       }
    //定义需要的局部变脸,根据obj的类型来调整target的类型
       var i, target = util.isType(obj,"array") ? [] : {},value,valueType;
       for(i in obj){
           value = obj[i];
           valueType = util.getType(value);
       //只有在明确执行深复制,并且当前的value是数组或对象的情况下才执行递归复制
           if(deep && (valueType === "array" || valueType === "object")){
               target[i] = copy(value);
           }else{
               target[i] = value;
           }
       }
       return target;
   }
参考资料:

https://www.cnblogs.com/tracylin/p/5346314.html

上一篇下一篇

猜你喜欢

热点阅读