javascript深拷貝與淺拷貝

在工作中會遇到各式各樣的與深拷貝淺拷貝相關(guān)的問題,其實造成這類問題的根本原因是javascript中,不同的數(shù)據(jù)類型的傳值方式不一致,首先我們先說一下js中都有哪些數(shù)據(jù)類型:

1.javascript的數(shù)據(jù)類型及傳值方式

  • 簡單數(shù)據(jù)類型
    簡單數(shù)據(jù)類型也就是值類型,簡單數(shù)據(jù)類型有:Undefined, Null,Boolean,Number,String.
    傳值方式:變量的交換等于在一個新的作用域創(chuàng)建一個新的空間,新空間與之前的空間互不相關(guān)和影響。
  • 復(fù)雜數(shù)據(jù)類型
    復(fù)雜數(shù)據(jù)類型也叫引用類型,常見的復(fù)雜數(shù)據(jù)類型有:Object、Array、Function。
    傳值方式:變量的交換,并不會創(chuàng)建一個新的空間,而是讓對象或方法和之前的對象或方法,同時指向一個原有空間(即一個地址)。就如同原來一個人有家門的鑰匙,之后這個人結(jié)婚了,就配了一把鑰匙給自己的妻子,這時候,兩個人共同有家的鑰匙,但是家還是一個家。

2.淺拷貝

什么是淺拷貝呢?我們來直接看代碼吧!

    var obj = {a: 10, b: 20, c: 30}
    var obj2 = obj
    obj2.b = 50
    console.log(obj) //{a: 10, b: 50, c: 30}
    console.log(obj2) //{a: 10, b: 50, c: 30}

復(fù)制一份obj為obj2,修改了obj2.b的值,由于這兩個對象所指向同一個地址,所以造成obj.b的值也跟著改變了,這就叫做淺拷貝。

3.深拷貝

我們希望改變復(fù)制過來的新值不對舊數(shù)據(jù)進(jìn)行修改,這就是深拷貝,那么我們要怎么做才可以深拷貝呢

方法1.Object.assign()
    var obj = {a: 10, b: 20, c: 30}
    var obj2 = Object.assign({}, obj) //第一個參數(shù)要為{}
    obj2.b = 50
    console.log(obj) //{a: 10, b: 20, c: 30}
    console.log(obj2) // {a: 10, b: 50, c: 30}

這個方法看起來不錯,使用起來也比較方便,但是這個方法的坑不??!只適用于一層的深度拷貝,對于多級的拷貝就歇菜了~例如

    var obj = {a: 10, b: {aa: 'aa', bb: 'bb'}, c: 30}
    var obj2 = Object.assign({}, obj)
    obj2.b.aa = 50
    console.log(obj) //{a: 10, b: {aa: 50, bb: 'bb'}, c: 30}  改變了
    console.log(obj2) // {a: 10, b: {aa: 50, bb: 'bb'}, c: 30}
方法2.JSON.parse(JSON.stringify())
    var obj = {a: 10, b: {aa: 'aa', bb: 'bb'}, c: 30}
    var obj2 = JSON.parse(JSON.stringify(obj))
    obj2.b.aa = 50
    console.log(obj) //{a: 10, b: {aa: 'aa', bb: 'bb'}, c: 30} 沒有變沒有變
    console.log(obj2) // {a: 10, b: {aa: 50, bb: 'bb'}, c: 30}

嗯嗯,這個方法解決了方法1存在的問題,但是不是就是完美的方法了呢?答案是一個非??隙ǖ腘O!!!!!!,那么這個方法存在什么樣的問題呢?首先它會拋棄對象的constructor。也就是深拷貝之后,不管這個對象原來的構(gòu)造函數(shù)是什么,在深拷貝之后都會變成Object。

這種方法能正確處理的對象只有 Number, String, Boolean, Array, 扁平對象,即那些能夠被 json 直接表示的數(shù)據(jù)結(jié)構(gòu)。也就是說,只有可以轉(zhuǎn)成JSON格式的對象才可以這樣用,像function沒辦法轉(zhuǎn)成JSON。

    var obj = {a: 10, b: function(){}, c: 30}
    var obj2 = JSON.parse(JSON.stringify(obj))
    console.log(obj.b) //function(){}
    console.log(obj2.b) //undefine
方法3.slice()或concat()
    var arr = ['a', 'b', 'c', 'd']
    var arr2 = arr.slice(0)
    arr2.push('haha')
    console.log(arr) // ["a", "b", "c", "d"]
    console.log(arr2) // ["a", "b", "c", "d", "haha"]

對數(shù)組進(jìn)行深度克隆可以使用slice()/concat()方法,由上面的例子可以看到這個方法針對一層的深度拷貝是可以的,但是對多級的拷貝的效果如何呢?

    var arr = ['a', 'b', ['aa', 'bb'], 'd']
    var arr2 = arr.slice(0)
    //var arr2 = arr.concat()
    arr2[2].push('haha')
    console.log(arr) // ["a", "b",['aa', 'bb','haha'], "d"]  改變了
    console.log(arr2) // ["a", "b",['aa', 'bb','haha'], "d"]

由上面例子可以看出slice()/concat()方法對多級的拷貝也是沒有效果的。

方法4.自定義方法

上面的方法或多或少都有這一些問題,為了使用更加方便,我們可以自定義一個方法deepcope進(jìn)行深度克隆。

    function deepcope(data) {
        var temp;
        if(data instanceof Array) { //克隆的是數(shù)組
            temp = []
            for(var i = 0, l = data.length; i < l; i++){
                temp.push(deepcope(data[i]))
            }
        } else if (data instanceof Object) { //克隆的是對象
            temp = {}
            for(var key in data){
                temp[key] = deepcope(data[key])
            }
        } else {  //克隆的既不是數(shù)組也不是對象則返回原數(shù)據(jù)
            temp = data
        }
        return temp
    }

方法寫好了現(xiàn)在我們來試試這個方法的效果如何,首先先嘗試一個數(shù)組的深度克?。?/p>

    //一層克隆
    var arr = ['a', 'b', 'c']
    var arr2 = deepcope(arr)
    arr2[2]='change'
    console.log(arr2) //['a', 'b', 'change']
    console.log(arr) //['a', 'b', 'c']

    //多層克隆
    var arr = ['a', 'b', [['aaa','bbb'], 'bb'], 'd']
    var arr2 = deepcope(arr)
    arr2[2][0][0]='change'
    console.log(arr2) //['a', 'b', [['change','bbb'], 'bb'], 'd']
    console.log(arr) //['a', 'b', [['aaa','bbb'], 'bb'], 'd']

我們自己寫的方法可以完美的進(jìn)行數(shù)組一層以及多層拷貝,那么針對對象的拷貝效果如何呢?我們再次嘗試一下:

    //一層克隆
    var obj = {a:'a', b:'b', c:'c'}
    var obj2 = deepcope(obj)
    obj2.b = 'change'
    console.log(obj2)   //{a:'a', b:'change', c:'c'}
    console.log(obj)   //{a:'a', b:'b', c:'c'}

    //多層克隆
    var obj = {a:'a', b:{aa:'aa', bb:{aaa:'aaa'}}, c:'c'}
    var obj2 = deepcope(obj)
    obj2.b.bb.aaa = 'change'
    console.log(obj2)  //{a:'a', b:{aa:'aa', bb:{aaa:'change'}}, c:'c'}
    console.log(obj)  //{a:'a', b:{aa:'aa', bb:{aaa:'aaa'}}, c:'c'}

不錯! deepcope方法也可以將對象進(jìn)行深度拷貝。那么對象和數(shù)組的混合效果如何?

    var obj = {a:'a', b:[{aa:'aa', bb: ['aaa', 'bbb']},'bb'], c:'c'}
    var obj2 = deepcope(obj)
    obj2.b[0].bb[0] = 'change'
    console.log(obj2)   // {a:'a', b:[{aa:'aa', bb: ['change', 'bbb']},'bb'], c:'c'}
    console.log(obj)   // {a:'a', b:[{aa:'aa', bb: ['aaa', 'bbb']},'bb'], c:'c'}

嗯!由以上示例我們可以得出結(jié)論:我們自定義的deepcope方法可以對數(shù)組以及對象進(jìn)行深度克隆!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容