柚子讴歌的博客

深拷贝与浅拷贝

2018/06/16

浅拷贝

浅拷贝指只是指向被复制的内存地址,如果原地址中对象被改变了,那么浅拷贝的对象也随之改变(假设B复制了A,当A被修改时,B也会改变)

demo:

let a = [1,2,3,4];
b = a;
console.log(a === b);    //判断a,b是否相等
a[2] = 1;
console.log(a,b);

深拷贝

深拷贝不仅将原对象的各个属性复制,而且将原对象各个属性所包含的对象递归复制到新对象上。

demo:

function deep(obj){
    let objClone = Array.isArray(obj)?[]:{};
    if(obj && obj === "object"){
        for(key in obj){
            if(obj.hasOwnProperty(key)){
                //判断obj元素是否为对象,是就进行递归拷贝
                if(obj[key] && typeof obj[key] === "object"){
                    objClone[key] = deep(obj[key]);
                }else{
                    //不是,进行浅拷贝
                    objClone[key] = obj[key];
                }
            }
        }    
    }
    return ojbClone;
}
let a = [1,2,3,4];
b = deep(a);
a[0] = 2;
console.log(a,b);

Why?

深拷贝与浅拷贝的不同其实在各自的定义中已经体现出来了,那它们是为什么会出现这些区别呢?

首先我们需要对基本数据类型(number,string,boolean,null,undefined)和引用数据类型(object,array,function,json等)有更深的了解:

对于基本数据类型来说,它会将名值存储在栈内存中

当我们let a = 1













栈内存
namevalue
a1

当我们b = a

















栈内存
namevalue
a1
b1

我们这时修改a =2,对b不会造成影响,但是它不是深拷贝,请记住,深拷贝针对的是较为复杂的object类型数据


而对于引用数据类型,它的名存在栈内存中,而值存在堆内存中,栈内存提供一个引用的地址来指向堆内存中的值

当我们使用b = a进行拷贝时,复制的只是a的引用地址,并非堆内存中的值

而当我们使用a[2] = 1时,由于a,b指向的是同一个地址,所以b也会受到影响,这就是浅拷贝

为了解决这个问题,我们可以在堆内存中将b的值也存储起来(类似于基本类型存储),就可以实现深拷贝【参考深拷贝函数

当然除了利用递归实现之外,我们也可以用json对象的parse和stringify方法实现

function deep(obj){
    let _obj = JSON.stringify(obj),
        objClone = JSON.parse(_obj);
    return ojbClone
}
let a = [0,1,[2,3],4],
    b = deep(a);
a[0] = 1;
a[2][0] = 1;
console.log(a,b);

还可以使用jquery的extend方法

$.extend([deep],target,object1[,objectN])

  • deep表示是否深拷贝,为true是深拷贝,为false是浅拷贝
  • target Object类型,是目标对象,其他对象的成员属性将被附加到该对象上
  • object1 objectN 可选,Object类型,第一个至第N个被合并的对象
let a = [0,1,[2,3],4],
    b = $.extend(true,[],a);
a[0] = 1;
a[2][0] = 1;
console.log(a,b);
CATALOG
  1. 1. 浅拷贝
  2. 2. 深拷贝
  3. 3. Why?