深拷贝与浅拷贝的 | 掘金·日新计划

54 阅读2分钟

JS中的变量类型分为 基本类型 和 引用类型;对基本类型进行复制操作会对值进行一份拷贝,而对引用类型赋值,则会进行地址的拷贝,最终两个变量指向同一份数据。

// 基本类型
var a = 1;
var b = a;
a = 2;
console.log(a, b); // 2, 1a b指向不同的数据

// 引用类型:指向同一份数据
var a = {c: 1};
var b = a;
a.c = 2;
console.log(a.c, b.c); // 2, 2 全是2a b指向同一份数据

其实深拷贝和浅拷贝都是针对的引用类型。浅拷贝就是只进行一层拷贝,深拷贝就是无限层级拷贝

例:
(先忽略shadowCopy\deepCopy的具体实现)

var a1 = {b: {c: {}}; 

var a2 = shadowCopy(a1); // 浅拷贝 
a2.b.c === a1.b.c // true

var a3 = deepCopy(a1); // 深拷贝 
a3.b.c === a1.b.c // false

一、浅拷贝

//对象浅拷贝
function shadowCopy(obj){
    if(typeof obj !== 'object') return ;
    var newObj;
    if(obj.constructor === Array){
        newObj = [];
    } else {
        newObj = {};
        newObj.constructor = obj.constructor;//保留对象的constructor属性
    }
    for(var prop in obj){
        if(obj.hasOwnProperty(prop)){
          newObj[prop] = obj[prop];
        }
    }
    return newObj;
}
var obj = {arr:[1,2],name:'bty'};
var obj2 = shadowCopy(obj);
console.log(obj.arr[0]);	//1
obj2.arr[0] = 9;
console.log(obj.arr[0]);   //9

上述代码实现了浅拷贝,会有不能正确实现数组的浅拷贝拷贝操作丢失了对象的constructor属性的问题。

二、深拷贝

那么深拷贝可能就需要层层递归,复制对象的所有属性,包括对象属性的属性的属性,有人想出了用JSON的解析实现,如下代码:

function deepCopy(obj){ 
if(typeof obj !== "object"){ return ;}
var str = JSON.stringify(obj); 
return JSON.parse(str); }

上面的方法不适用的条件是

  • 需要考虑把函数,正则等特殊数据类型复制
  • 当前对象不支持JSON
  • 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}

这个时候还是要层层递归来不同情况不同分析来考虑的,最终的方案如下:

//对象深拷贝
function deepCopy(obj){
    var newObj = obj.constructor === Array ? []:{};
    newObj.constructor = obj.constructor;
    if(typeof obj !== "object"){ 
        return ;
    } else if(window.JSON){
        newObj = JSON.parse(JSON.stringify(obj));//若需要考虑特殊的数据类型,如正则,函数等,需把这个else if去掉即可
    } else {
        for(var prop in obj){
            if(obj[prop].constructor === RegExp ||obj[prop].constructor === Date){
                newObj[prop] = obj[prop];
            } else if(typeof obj[prop] === 'object'){
                newObj[prop] = deepCopy(obj[prop]);//递归
            } else {
                newObj[prop] = obj[prop];
            }
        }
    } 
    return newObj;
}

var obj = {arr:[1,2],name:'bty'};
var obj2 = deepCopy(obj);
console.log(obj.arr[0]);	//1
obj2.arr[0] = 9;
console.log(obj.arr[0]);   //1