1. 程式人生 > >JavaScript淺拷貝與深拷貝淺析

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裡的值是不會跟著發生改變的。這裡涉及到了遞迴呼叫,有不明白的童鞋可以看下遞迴相關的資料。這就完成了對一個物件的深拷貝。

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