深拷贝和浅拷贝

2,741 阅读3分钟

欢迎大家关注我的 github

基本类型和引用类型

基本类型:存放在栈内存中的数据段,因为是按值存储,可以直接访问和修改

引用类型:存放在堆内存中的对象。在栈内存中变量保存的是一个指针,指向对应在堆内存中的地址。当访问引用类型的时候,要先从栈中取出该对象的地址指针,然后再从堆内存中取得所需的数据。

传值和传址

基本类型是按值存放,可以直接访问是传值

引用类型是在栈中存放的是地址,需要按照地址再去访问相应的值是传址

var a = [1, 2, 3, 4, 5]
var b = a;
var c = a[0]

alert(b) // 1,2,3,4,5
alert(c) // 1

// 改变数值

b[4] = 6
c = 7
alert(a[4]) // 6
alert(a[0]) // 1

a赋值给b,在栈内存中其实是将a的地址复制一份给b,此时a和b访问的都是同一个地址的对象,所以修改b[4]会直接修改堆内存中的对象的值,a和b都同时访问该值,a和b就产生了关联。如何解除关联,可以看c是通过直接赋值堆内存中的对象的值(一个基本类型),这就是按值传递了,所以c不会和a、b建立联系

所以简单的说就是,深拷贝一定都是按值传递,浅拷贝一定有按址传递

浅拷贝

var a = {
    key1:"11111"
}
function Copy(p) {
    var c = {}; //(在堆内存中新生成一个空对象,这样来杜绝关联)
    for (var i in p) { 
        c[i] = p[i];
    }
    return c;
}
a.key2 = ['小辉','小辉'];
var b = Copy(a);
b.key3 = '33333';
alert(b.key1);     //1111111
alert(b.key3);    //33333
alert(a.key3);    //undefined

由于a.key2是个引用类型,所以copy的时候a.key2是按址传递的,就会导致修改a.key2的值时,b.key2的值也会变化,因为是按址传递的。因为a和b会有关联,所以说这次拷贝是一次浅拷贝

深拷贝

将刚才的代码改成这样:

var a = {
    key1:"11111"
}
function Copy(p) {
    var c = {};  //(在堆内存中新生成一个空对象,这样来杜绝关联)
    for (var i in p) { 
        c[i] = p[i];
    }
    return c;
}
var b = Copy(a);
b.key1 = '33333';
alert(a.key1);     //1111111
alert(b.key1);    //33333

这次拷贝因为copy的是基本类型,是按值传递,所以这是深拷贝

此时的情况是知道当前对象的结构,如果不知道当前对象的结构如何进行深拷贝呢?可以通过递归进行深拷贝

function copy(a, b) {
    var c = c || {}
    for(var i in a) {
        if(typeof a[i] == 'object') {
            if(a[i].constructor == Array) {
                copy(a[i], c[i])
            } else if(a[i].constructor == Object) {
                copy(a[i], c[i])
            }
        } else {
            c[i] = a[i]
        }
    }
    return c
}
var a = {
    key1: '11111',
    key2: [1,2,3,4,5]
}
var b = copy(a, b)
b.key2 = [2,2,2,2,2]
alert(a.key2)   // 1,2,3,4,5
alert(b.key2)   // 2,2,2,2,2

a和b之间copy时都是按值传递,所以a和b没有关联,所以这是一次深拷贝



参考资料:

http://www.cnblogs.com/lsgxeva/p/7985583.html