淺拷貝和深拷貝

2022-06-22 23:42:12 字數 4331 閱讀 5005

在介紹淺拷貝和深拷貝的區別之前,先看乙個例子,或許可以方便我們理解:

1/*2

** example 13*/

4 let value_1 = 1;

5 let value_2 =value_1;

67 value_2 = 2;

8 console.log(value_1); //

19 console.log(value_2); //210

11/*

12** example 2

13*/

14 let value_3 =;

18 let value_4 =value_3;

1920 value_4.name = "may";

21 console.log(value_3.name); //

may22 console.log(value_4.name); //

may23

24/*

25** example 3

26*/

27 let value_5 =;

31 let value_6 =;

3536 value_6.name = "may";

37 console.log(value_5.name); //

peter

38 console.log(value_6.name); //

may

其中,example 1 和 example 2 就是我們平時用得最多的拷貝,也就是淺拷貝。

ps:由於淺拷貝和深拷貝一般都是針對於物件以及陣列而言的,example 1 只用於對比。

通過上面的 example 2 我們可以看到,如果我們是直接將乙個陣列/物件賦值給另外乙個變數,當我們對其中乙個變數的值進行修改的時候,另外乙個的值也會隨之改變,究其原因其實就是在我們進行賦值的時候,實際上是將變數1的引用,賦值給了變數2,換句話說,實際上我們是將變數1的位址賦值給了變數2,所以才會出現上面的情況,這也就是我們常說的淺拷貝。

我們先來看一下淺拷貝、深拷貝的定義:

所以當我們希望複製乙個物件/陣列的值,又不希望對其造成修改時,就需要用到深拷貝了。可以看一下上面的 example 3 ,就是乙個十分簡單的深拷貝寫法,但是這種寫法十分具有侷限性,當物件/陣列中的元素足夠多的時候,我們還能用這種方法來進行深拷貝嗎?答案很明顯是不能的。聰明的你肯定想到了,「那我可以對原物件/陣列進行遍歷,再將其中每乙個值賦值給新變數啊!」。那我們抱著實驗出真知的態度,來看一下下面這段**:

1

function

deepcopy(data)

11return

newdata;

12 } else;14

for (let i in

data)

17return

newdata;18}

19}2021 let array1 = [1, 2, 3, 4];

22 let array2 =deepcopy(array1);

23 console.log(array1); //

[1, 2, 3, 4]

24 console.log(array2); //

[1, 2, 3, 4]

25 array2[2] = 5;

26 console.log(array1); //

[1, 2, 3, 4]

27 console.log(array2); //

[1, 2, 5, 4]

2829 let object1 =;

33 let object2 =deepcopy(object1);

34 console.log(object1); //

35 console.log(object2); //

36 object2.name = "lily";

37 console.log(object1); //

38 console.log(object2); //

上面的**乍一看,似乎沒什麼問題,而且也可以達到我們深拷貝的效果,但是如果將測試集換成以下**再來驗證以下,似乎又會出現不同的結果:

1 let array3 = [1, [2, 3], 4];

2 let array4 =deepcopy(array3);

3 array4[1][1] = 5;

4 console.log(array3); //

[1, [2, 5], 4]

5 console.log(array4); //

[1, [2, 5], 4]

通過上面的測試我們可以看到,當原陣列/物件中還包含著陣列/物件時,只用單獨一層拷貝是完全不夠的,原因我們上面已經講過了,拷貝過去的只是它的位址,所以當其中乙個變數發生改變時另外乙個也會跟著改變。那我們嘗試來變通一下,當我們在遍歷陣列/物件的子項時,當發現其子項是陣列/物件時,也對它進行一次深拷貝不就可以了嗎。我們來嘗試一下,對上面的**稍作修改:

function

deepcopy(data)

else

}return

newdata;

} else

;

for (let i in

data)

else

}return

newdata;

}}let array1 = [1, [2, [3, [4, [5, 6, ]]]]];

let array2 =deepcopy(array1);

console.log(array1);

//[1, [2, [3, [4, [5, 6, ]]]]]

console.log(array2); //

[1, [2, [3, [4, [5, 6, ]]]]]

array2[1][1][1][1][2].a = 10;

array2[1][0] = 10;

console.log(array1);

//[1, [2, [3, [4, [5, 6, ]]]]]

console.log(array2); //

[1, [10, [3, [4, [5, 6, ]]]]]

修改之後,無論陣列/物件中鑲嵌著多少層子陣列/物件,我們都可以將其完整的深拷貝下來。深拷貝在實際開發環境中用到的情況也很多,比如說我自己開發過的,從伺服器獲取到資料之後,有時候可能需要對資料進行處理之後再進行顯示,但我又需要保留原有資料進行比較,深拷貝在這種情況下遍會發揮很重要的作用了。另外,淺拷貝和深拷貝也是前端面試中的常考題,需要加深對這兩種拷貝方式的理解。

最後,分享一些在別的博主那裡看到的,使用一些「小技巧」來進行深拷貝。

方法一:使用slice()來進行深拷貝。

我們都知道,在js中slice(start, end)是用來對陣列進行切割,當不傳入任何引數時,預設返回乙個長度和原陣列相同的新陣列,也就是所謂的深拷貝。但是這種方法只適用於對一維陣列進行深拷貝,對物件(因為物件沒有slice()方法)以及多維陣列(理由同上面講的一樣)無效。

方法二:使用concat()方法來進行深拷貝。

concat(arr1, arr2, ..., arrn)方法是用來連線多個陣列,但是該方法不會改變現有的陣列,而是只會返回被連線陣列的乙個副本,所以也可以用來對一維陣列進行深拷貝。但是弊端也很明顯,跟上面方法一所說的是一樣。

方法三:object.assign()

在es6中,提供了object.assign(target, source1, source2)這個方法來進行深拷貝。它主要是用於物件的合併,將源物件(source)的所有可列舉屬性,複製到目標物件(target),並返回合併後的target。所以我們可以使用 copyobj = object.assign({}, obj) 這段**來將obj中的內容深拷貝到copyobj中,這段**將會把obj中的一級屬性都拷貝到 {}中,然後將其返回賦給copyobj。但是這個方法也是只能用來深拷貝只有一級的物件,當物件的子項中含有陣列/物件時,這種拷貝方式也會失敗。

總結一下,雖然上面所說的三種方法用起來都十分方便,但是使用也十分具有侷限性,只能用於一維陣列或只有一級的物件。

但是呢,其實還有一種很簡單的方法,可以對多維陣列以及多級物件進行深拷貝,那就是使用json.stringify()和json.parse()。這個方法的原理很簡單,先使用json.stringify()將陣列/物件轉換成字串,再使用json.parse()將該字串轉換回物件,也就是重新分配一塊空間給這個物件,所以拷貝出來的物件與原先物件互不影響。雖然這種方法很方便,但是投機取巧總歸來說也不是太好,還是建議大家要自己會寫使用遍歷+遞迴的方法來進行深拷貝。

深拷貝和淺拷貝

淺拷貝就是物件的資料成員之間的簡單賦值,如你設計了乙個沒有類而沒有提供它的複製建構函式,當用該類的乙個物件去給令乙個物件賦值時所執行的過程就是淺拷貝,如 class a a private int data int main 這一句b a 就是淺拷貝,執行完這句後b.data 5 如果物件中沒有其他...

淺拷貝和深拷貝

以下情況都會呼叫拷貝建構函式 乙個物件以值傳遞的方式傳入函式體 例如 已知class a,class b void func a a void func a a func b b 此時函式對b的操作是呼叫拷貝建構函式後的臨時拷貝物件。多數傳指標 乙個物件以值傳遞的方式從函式返回 如 return b...

深拷貝和淺拷貝

ios提供了copy和mutablecopy方法,顧名思義,copy就是複製了乙個imutable的物件,而mutablecopy就是複製了乙個mutable的物件。以下將舉幾個例子來說明。1 系統的非容器類物件 這裡指的是nsstring nsnumber等等一類的物件。nsstring stri...