JavaScript淺拷貝與深拷貝
前言
淺拷貝和深拷貝是在處理物件和陣列時非常重要的概念,
了解它們的差異可以幫助我們選擇最適合的複製方式,以滿足特定的需求和性能要求。
用此篇來紀錄,如果是現在的我,會怎麼來理解淺拷貝與深拷貝。
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()。
原理是透過把物件轉成字串後,再轉成物件的方式,讓它完全跟原本的變數獨立開來。