JS深入基础之深拷贝与浅拷贝

106 阅读2分钟

深拷贝和浅拷贝


数组的浅拷贝

slice和concat方法就可以实现

var arr = ['old', 1, true, null, undefined];

var new_arr = arr.concat();
var new_arr = arr.slice();

new_arr[0] = 'new';

console.log(arr) // ["old", 1, true, null, undefined]
console.log(new_arr) // ["new", 1, true, null, undefined]

但是如果数组嵌套了对象或者数组的话,比如:

var arr = [{old: 'old'}, ['old']];

var new_arr = arr.concat();

arr[0].old = 'new';
arr[1][0] = 'new';

console.log(arr) // [{old: 'new'}, ['new']]
console.log(new_arr) // [{old: 'new'}, ['new']]    

这是因为对象和数组都是引用类型的值。也就是用以上这两种方法,克隆的都不彻底。面对对象和数组的时候,实际上拷贝的是他们的引用。

我们把这种复制引用的拷贝方法称之为浅拷贝,与之对应的就是深拷贝,深拷贝就是指完全的拷贝一个对象,即使嵌套了对象,两者也相互分离,修改一个对象的属性,也不会影响另一个。

数组的深拷贝

首先讲一个最简单的方法~!他不仅能适用于嵌套的数组还适用于嵌套的对象!

var arr = ['old', 1, true, ['old1', 'old2'], {old: 1}]
var new_arr = JSON.parse(JSON.stringify(arr))
console.log(arr)    // ['old', 1, true, ['old1', 'old2'], {old: 1}]

就是有一个问题,不能拷贝函数,我们做个试验:

var arr = [function(){
    console.log(a)
}, {
    b: function(){
        console.log(b)
    }
}]

var new_arr = JSON.parse(JSON.stringify(arr));

console.log(new_arr);  // {null,{}} 变成空的了!!  

以上是一些技巧类的实现,现在我们来自己写一个浅拷贝代码

// 浅拷贝

function shallowCopy(obj){
    if(typeof obj != 'object'){
        return
    }
    var newobj = Array.isArray(obj)?[]:{}
    for(var key in obj){
        if(obj.hasOwnProperty(key)){
            newobj[key] = obj[key]
        }
    }
    return new obj
}

那如何实现一个深拷贝?思路就是在赋值的时候先判断一下这个值是不是一个object。如果是,那么就递归他

function deepCopy(obj){
    if(typeof obj != 'object'){
        return
    }
    var newobj = Array.isArray(obj)?[]:{}
    for(var key in obj){
        if(obj.hasOwnProperty(key)){
            newobj[key] = typeof obj[key] == 'object'? deepCopy(obj[key]) : obj[key]
        }
    }
    return newobj
}

来测试一下

var arr = ['old', 1, true,null,undefined,[1,2],{a:'b',c:'d'}
,function(){console.log('a')},{'shit':function(){console.log('shit')}}];

function deepCopy(obj){
    if(typeof obj != 'object'){
        return
    }
    var newobj = Array.isArray(obj)?[]:{}
    for(var key in obj){
        if(obj.hasOwnProperty(key)){
            newobj[key] = typeof obj[key] == 'object'? deepCopy(obj[key]) : obj[key]
        }
    }
    return newobj
}

var newarr = deepCopy(arr)
console.log(newarr) 
// [ 'old',
1,
true,
{},
undefined,
[ 1, 2 ],
{ a: 'b', c: 'd' },
[Function],
{ shit: [Function: shit] } ]

发现了一个问题,就是null被转换为空对象了

最终版本:

function deepCopy(obj){
    if(typeof obj != 'object'){
        return
    }
    var newobj = Array.isArray(obj)?[]:{}
    for(var key in obj){
        if(obj.hasOwnProperty(key)){
            if(Object.prototype.toString.call(obj[key]).slice(8,-1) == 'Null'){
                newobj[key] = null
            }else{
                newobj[key] = typeof obj[key] == 'object'? deepCopy(obj[key]) : obj[key]
            } 
        }
    }
    return newobj
}