Js深度拷貝解決雙向繫結問題

龍雨溪發表於2018-10-26

當我們利用v-bind:來繫結屬性向子元件傳遞物件的時候,有時候我們需要子元件改變的時候不改變父元件的值,一般可以利用JSON.stringify(JSON.parse(jsonstr))將傳遞來的物件賦值到子元件自己的data,這樣做的原理是對傳過來的值重新賦予一個空間,從而解決雙向繫結。,但是es6有一個深度賦值的方法也可以解決這個問題, let obj= Object.assign({}, obj)也可以解決。

比如一個陣列(array)淺度拷貝是當陣列a變數成陣列b的時候,b改變裡面的陣列數值的時候,a也隨著改變.

深度拷貝是噹噹陣列a變數成陣列b的時候,b改變裡面的陣列數值的時候,a裡面的陣列陣列不隨著改變,

var arr = ["a", "b", "c", "d", "e"];      
var Arr = JSON.stringify(arr); //先轉化為string字串的型別
      
var Brr = JSON.parse(Arr); //在解析字串的型別
Brr[1] = 'h';             //這樣修改Brr中的陣列的時候就不會影響到arr裡面陣列的值
console.log('arr:' + arr); //結果是arr:a,b,c,d,e
console.log("Arr:" + Brr); //結果是Arr:a,b,c,d,e
複製程式碼

那麼為什麼淺度拷貝會改變a的陣列值而深度拷貝則不會呢?

因為淺度拷貝指向的是同一個記憶體,而深度拷貝是增加了一個新的記憶體,所以不會影響到原來a的記憶體, 所 以就不會改變原來的值 eg.

var arr = ["a", "b", "c", "d", "e"];      
var Arr = arr;    
Arr[1] = 'h';     
console.log('arr:' + arr);  //arr的下標1的‘b’也變成了‘h’ 結果是:arr:a,h,c,d,e
console.log("Arr:" + Arr); //結果是:Arr:a,h,c,d,e
複製程式碼

陣列的深拷貝

對於陣列的深拷貝常規的有三種方法:

方法一:遍歷複製

var arr = ["a", "b"], arrCopy = [];
for (var item in arr) arrCopy[item] = arr[item];
arrCopy[1] = "c";
arr   // => ["a", "b"]
arrCopy   // => ["a", "c"]
複製程式碼

考慮偽多維陣列可以寫成函式形式:

function arrDeepCopy(source){
    var sourceCopy = [];
    for (var item in source) sourceCopy[item] = typeof source[item] === 'object' ? arrDeepCopy(source[item]) : source[item];
    return sourceCopy;
}
複製程式碼

這種方法簡單粗暴,但是利用JS本身的函式我們可以更加便捷地實現這個操作。

方法二:slice()

可以參考 W3School 對 slice() 方法的描述:slice() 方法可從已有的陣列中返回選定的元素。

呼叫格式為:

arrayObject.slice(start,end) 方法返回一個新的陣列,包含從 start 到 end (不包括該元素)的 arrayObject 中的元素。該方法並不會修改陣列,而是返回一個子陣列。

在這裡我們的思路是直接從陣列開頭截到尾:

arrCopy = arr.slice(0); arrCopy[1] = "c"; arr // => ["a", "b"] arrCopy // => ["a", "c"] 可以看出成功建立了一份原陣列的拷貝。

方法三:concat()

可以參考 W3School 對 concat() 方法的描述:concat() 方法用於連線兩個或多個陣列。

呼叫格式為: arrayObject.concat(arrayX,arrayX,......,arrayX) 該方法不會改變現有的陣列,而僅僅會返回被連線陣列的一個副本。

使用這種方法的思路是我們用原陣列去拼接一個空內容,放回的便是這個陣列的拷貝:

arrCopy = arr.concat();
arrCopy[1] = "c";
arr   // => ["a", "b"] 
arrCopy   // => ["a", "c"]
複製程式碼

物件的深拷貝

對於陣列的深拷貝我們有了概念,那麼一般物件呢?

我們給出一個物件:

var obj = { "a": 1, "b": 2 };
複製程式碼

同樣做測試:

var objCopy = obj;
objCopy.b = 3;
obj   // => { "a": 1, "b": 3 }
objCopy   // => { "a": 1, "b": 3 }
複製程式碼

同樣,簡單的賦值運算只是建立了一份淺拷貝。

而對於物件的深拷貝,沒有內建方法可以使用,我們可以自己命名一個函式進行這一操作:

var objDeepCopy = function(source){
    var sourceCopy = {};
    for (var item in source) sourceCopy[item] = source[item];
    return sourceCopy;
}
複製程式碼

但是對於複雜結構的物件我們發現這個函式並不適用,例如:

var obj = { "a": { "a1": ["a11", "a12"], "a2": 1 }, "b": 2 };
複製程式碼

所以需要進行一點修改:

var objDeepCopy = function(source){
    var sourceCopy = {};
    for (var item in source) sourceCopy[item] = typeof source[item] === 'object' ? objDeepCopy(source[item]) : source[item];
    return sourceCopy;
}
var objCopy = objDeepCopy(obj);
objCopy.a.a1[1] = "a13";
obj   // => { "a": { "a1": ["a11", "a12"], "a2": 1 }, "b": 2 }
objCopy   // => { "a": { "a1": ["a11", "a13"], "a2": 1 }, "b": 2 }
複製程式碼

3、物件陣列的深拷貝

如果再考慮更奇葩更復雜的情況,例如我們定義:

var obj = [{ "a": { "a1": ["a11", "a12"], "a2": 1 }, "b": 2 }, ["c", { "d": 4, "e": 5 }]]; 這是一個由物件、陣列雜合成的奇葩陣列,雖然我們平時寫程式基本不可能這麼折騰自己,但是可以作為一種特殊情況來考慮,這樣我們就可以結合之前說的方法去擴充拷貝函式:

var objDeepCopy = function (source) {
    var sourceCopy = source instanceof Array ? [] : {};
    for (var item in source) {
        sourceCopy[item] = typeof source[item] === 'object' ? objDeepCopy(source[item]) : source[item];
    }
    return sourceCopy;
}
var objCopy = objDeepCopy(obj);
objCopy[0].a.a1[1] = "a13";
objCopy[1][1].e = "6";
obj   // => [{ "a": { "a1": ["a11", "a12"], "a2": 1 }, "b": 2 }, ["c", { "d": 4, "e": 5 }]]
objCopy   // => [{ "a": { "a1": ["a11", "a13"], "a2": 1 }, "b": 2 }, ["c", { "d": 4, "e": 6 }]]
複製程式碼

相關文章