Js中的深拷贝与浅拷贝

闲话

回看一个多月都没写博客了,最近在忙于给新人交接,其中就遇到了深拷贝的问题。今天抽空来聊聊深拷贝与浅拷贝。

引子

Js里有两种数据类型,基本数据类型和引用数据类型。深拷贝、浅拷贝一般都是针对引用数据类型的。
请看代码:

var a = 1;
var b = a;
a = 2;
console.log(a); // 2
console.log(b); // 1

对于基本数据类型赋值操作,b 复制了 a 的值,而不是引用。即保存 b 的值与 a 的值的内存空间是完全独立的。

var arr1 = [1,2,3,4];
var arr2 = arr1;

arr1.push(5);
console.log(arr1); // [1,2,3,4,5]
console.log(arr2); // [1,2,3,4,5]

arr2.push(6);
onsole.log(arr1); // [1,2,3,4,5,6]
console.log(arr2); // [1,2,3,4,5,6]

然而,对于引用数据类型的赋值操作,arr2 仅仅是复制了 arr1的引用(也可以称之为指向 arr1 内存地址的指针)。简单来说,就是 arr1 与 arr2 指向了同一个内存空间。

正题

浅拷贝

概念:浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。
例:

/*
 * copyTarget:要拷贝的对象
*/
function shallowCopy(copyTarget) {
    var obj = {};
    for (var key in copyTarget) {
        obj[key] = copyTarget[key];
    }
    return obj;
}

var json1 = {
    'name': '张三', 
    'family': {
        'children': '张三三','wife': '李四'
    }
}

var json2 = shallowCopy(json1);

// before
console.log(json2); 

// after
json1.family['father'] = '张一'
console.log(json1);
console.log(json2);

由此可以看出,浅拷贝仅仅拷贝了基本类型的数据,对于引用类型数据,则指向被复制的内存地址,若原地址中的对象发生改变,那么浅复制出来的对象也会相应改变。

深拷贝

有了浅拷贝的理解,那么深拷贝可概括为:为引用类型数据成员另辟了一个独立的内存空间,实现真正内容上的拷贝。

例:

/*
 * copyTarget:要拷贝的对象
*/
function deepCopy(copyTarget) {
    var obj = {};
    for(var key in copyTarget) {
        // 先判断obj[key]是否为对象
        if(typeof copyTarget[key] === "object"){
            // 递归
            obj[key] = deepCopy(copyTarget[key]);
        } else {
            // 如果不是对象,直接赋值即可
            obj[key] = copyTarget[key];
        }
    }
    return obj;
}

var json1 = {
    'name': '张三', 
    'family': {
        'children': '张三三','wife': '李四'
    }
}

var json2 = deepCopy(json1);

// before
console.log(json2); 

// after
json1.family['father'] = '张一'
console.log(json1);
console.log(json2);

拓展

深复制可以用JSON的方式:JSON.parse(JSON.stringify(obj))
但是JSON复制会忽略掉值为undefined以及函数表达式。

例:

var obj = {
    a: 1,
    b: 2,
    c: undefined,
    sum: function() { return a + b; }
};

var obj2 = JSON.parse(JSON.stringify(obj));
console.log(obj2); //Object {a: 1, b: 2}