JavaScript淺拷貝與深拷貝淺析

          在JavaScript中,變量的拷貝是無處不在的,而且也是不可或缺的,我們常常使用框架自帶的拷貝方法,比如angular1.x自帶的,angular.copy(),有時候卻不知道使用純JavaScript如何進行拷貝,現在我們就來說一下我個人對於淺拷貝和深拷貝的見解:

         1.淺拷貝

            簡單的來說,淺拷貝就是增加了一個'指針'指向已存在的內存(JavaScript並沒有指針的概念,這裏只是用於輔助說明),淺拷貝只是拷貝了內存的地址,子類的屬性指向的是父類屬性的內存地址,當子類的屬性修改後,父類的屬性也會隨之被修改。我們來看下一個例子:

var arrayA = [ 1,2,3,4,5 ];
var arrayB = arrayA ;
var str = 'hello'
arrayA.push(str) ;
console.log(arrayA);// [1, 2, 3, 4, 5, "hello"]console.log(arrayB);// [1, 2, 3, 4, 5, "hello"] 這就是一個簡單的淺拷貝的例子,首先聲明瞭一個arrayA數組,然後又聲明一個arrayB數組,並把arrayA數組賦值給arrayB數組,然後往arrayA裏push一個元素,結果arrayB裏的元素也跟着改變了,這就是淺拷貝。下面我給出一張圖來輔助理解:



      2.深拷貝

         深拷貝就是增加一個“指針”,並申請一個新的內存,並且讓這個新增加的“指針”指向這個新的內存地址,使用深拷貝,在釋放內存的時候就不會像淺拷貝一樣出現重複釋放同一段內存的錯誤,當我們需要複製原對象而又不能修改元對象的時候,深拷貝就是一個,也是唯一的選擇。我們來看一下例子:

var arrayA = [ 1,2,3,4,5 ];
var arrayB = [] ;
arrayA.forEach ( function (element){
    arrayB.push(elemnt);
})
var str = 'hello'
arrayA.push(str) ;
console.log(arrayA);// [1, 2, 3, 4, 5, "abc"]
console.log(arrayB);// [1, 2, 3, 4, 5]
這似乎是解決了淺拷貝所存在的問題。但是我們修改一下

var arrayA = [ 1,2,3,4,5 ];
var arrayB = [] ;
var obj = {name : 'unix'}
arrayA.push(obj) ;
arrayA.forEach(function (element){ arrayB.push(element);})
arrayB[5].name = 'Alex'console.log(arrayA);// [1, 2, 3, 4, 5, {name:'Alex'}]
console.log(arrayB);// [1, 2, 3, 4, 5, {name:'Alex'}]

這裏修改arrayB中的元素的時候,arrayA中的元素卻跟着改變,是爲什麼呢?我們來看下面這張圖輔助理解一下:


這裏的arrayA和arrayB中的最後一個元素,這個元素是一個對象,指向的是同一段內存地址,所以當修改其中一個元素對象的值時,導致了另一個的值也跟着發生改變,但是如果新增加的元素不是一個對象,而是一個字符串,或者一個數字,這時候是沒問題的。有問題我們是需要去解決的,對於第一種情況,我們使用同樣的方式去解決它。既然新增加的元素是一個字符串或者一個數字的情況下,改變一個元素的值不會引發另一個元素的值的改變,所以我們就使用這種方式去解決,解決方案如下所示:

function copy( sourceObj , c) {
    var c = c || ( Array.isArray(sourceObj) ? [ ] : {} );
    for (var i in sourceObj) {
        if (typeof sourceObj[i] === 'object') {
            c[i] = Array.isArray(sourceObj[i])  ? [] : {};
            copy (sourceObj[i], c[i]);
        } else {
            c[i] = sourceObj[i];
        }
    }
    return c;
}
var arrayA = [1,2,3,4,5];
var  obj = {name:'Alex'};
arrayA.push(obj)
var arrayB = [];
copy(arrayA,arrayB);
arrayB[5].name = 'Tom'
console.log(arrayA);// [1, 2, 3, 4, 5, "Alex"]
console.log(arrayB);// [1, 2, 3, 4, 5, "Tom"]
我們先定義一個copy函數,傳入兩個參數,第一個參數是原對象,第二個參數是複製的對象,我們循環原對象,看原對象中的元素的類型是否是對象(Object),如果是的話我們再使用遞歸調用,copy這個對象,如果不是對象,直接賦值。最後返回copy後的對象,也就是這裏的arrayB,當我們修改arrayB中的name的值時,arrayA裏的值是不會跟着發生改變的。這裏涉及到了遞歸調用,有不明白的童鞋可以看下遞歸相關的資料。這就完成了對一個對象的深拷貝。

          淺拷貝比較容易理解,當然深拷貝也是容易理解的,只是得注意拷貝上面說的那種情況。就沒問題了。有任何問題,歡迎提問



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章