前言

淺拷貝和深拷貝是在處理物件和陣列時非常重要的概念,

了解它們的差異可以幫助我們選擇最適合的複製方式,以滿足特定的需求和性能要求。

用此篇來紀錄,如果是現在的我,會怎麼來理解淺拷貝與深拷貝。

JS 有分成基本型別跟物件型別

基本型別(Primitive) ⇒

如:number、string、boolean、null、undefined。

純值的部分都含括在裡面,包括數字、字串、true、false。

基本型別不會碰到淺拷貝與深拷貝的問題。

原因是因為,複製給變數值,就直接賦值,新複製的不會影響原本的。

用實際程式碼來解釋:

這樣不會有大太問題,但是物件型別就沒辦法有相同效果。

物件型別(Object) ⇒

包括 陣列、物件、函式。

用陣列的方式做例子:

會發現修改了 array2,array 也會被改變。

其實原因是指向問題,物件型別跟基本型別,在傳遞參數的方式不同,導致變數指向記憶體的位置也會不同形式。

型別的不同,會影響指向

基本型別,在賦值傳遞參數時是「 複製值 (value) 」。

物件型別,在賦值傳遞參數時是「 複製地址 (address) 」。

pass by value 的情況下:

原始變數跟新的變數所指向的記憶體位置不一樣,傳遞的是當前那個值,把這個值給傳遞到新的記憶體空間。

pass by reference 的情況下:

在這種情況下,原始變數跟新的變數所指向的記憶體位置會是相同的,傳遞時只是讓新變數的指向成原始變數的記憶體位置。

也就因此,所以物件型別賦值變數時,變數會指向同一個記憶體位址,當改動新變數裡面值的時候,也會跟著影響到原始變數。

而淺拷貝與深拷貝所要解決的,就是希望在物件型別時,可以做到像是基本型別那樣,單純傳值的效果,原始變數跟新變數彼此之間是獨立的,改 A 不會動 B。

JS 中有很多方式可以做到淺拷貝

  • 展開運算子,也就是…
  • Object.assign()
  • array method 方式(map、filter、slice、concat)

用展開運算子來詳細看看效果:

確實不會互相影響了,透過這種展開運算子方式就可以解決指向問題,但是並沒有把問題完全解掉,

這種淺拷貝的方式只能解「第一層」狀況。

假如物件或陣列裡面又有物件資料,就會有相同問題。

怎麼會這樣? 那該怎麼辦?

這時候就是該深拷貝出動的時候了。

兩者差別,淺拷貝 V.S. 深拷貝

淺拷貝就像複製了外層的外殼,但內層的元素還是指向同一個地方。

深拷貝就是完全複製了所有內容,形成了一個全新的、獨立的資料結構,
與原始資料互不影響。

所以深拷貝就等於完全變成了基本型別的那種傳遞參數方式(傳值),而淺拷貝則是第一層(外層)能夠有此效果,第二層之後還是會回到物件型別的傳遞參數方式(傳址),要是對第二層之後的資料做改動,還是會去影響到原始變數。

複雜性:

「淺拷貝通常比深拷貝簡單和快速,因為它僅複製外層結構。」

適用場景:

「淺拷貝通常適用於不需要修改內層物件或陣列的情況。」

「深拷貝適用於需要完全獨立複製且不影響原始資料的情況。」

記憶體佔用:

「淺拷貝不複製內層元素,因此通常佔用較少的記憶體。而深拷貝則會複製所有內層元素,因此可能佔用更多的記憶體空間。」

非純值:

「淺拷貝和深拷貝通常主要用於處理物件和陣列等非純值。」

我所知道的深拷貝方式 JSON.parse(JSON.stringify())

用 JSON.stringify() 及 JSON.parse()。

原理是透過把物件轉成字串後,再轉成物件的方式,讓它完全跟原本的變數獨立開來。

參考資料