JS 觀念篇 -- 深淺拷貝
前言
這次要講一個我在做專案時一直被他搞死的概念
深拷貝與淺拷貝
雖然已經知道這個概念
相關文章也看到好幾次了
但每次還是會在專案中卡好久好久找不到 bug
最後才發現說:「啊 原來又是這個問題啊」
Call by value/reference
首先要釐清的觀念是 call by value 跟 call by reference
在 javascript 中物件是採用 call by reference
也就是例如當你把已存在記憶體中的物件指派到a
變數時a
會指向該記憶體位置,而不是複製其值
讓我們看個例子
1 | const A = {num: 1} |
當我互相比較這三個物件時
會發現A
和B
明明內容相同,卻回傳 false
只有當C
是從A
assign 來的時候才得到 true
因為其實 A 和 C 是共用同一個記憶體
而物件的比較是去看記憶體位置
所以得到 true
而A
和B
雖然內容一樣,卻因為都是新創的物件
他們會占用不同的記憶體位置
對 JS 來說就是不一樣的囉
淺拷貝
上述的 const C = A
就是淺拷貝的一種
他沒辦法做到完全的複製
而是在「某程度」上保有指向相同記憶體的特性
1 | const A = {num: 1} |
我只更動A
,結果B
的內容也改了
很多時候出現 bug 就是因為這個原因
至於剛剛為什麼要強調「某程度」呢?
因為有些做法只能在第一層上進行深拷貝
當你的物件結構更複雜時就會出問題
來看一個被誤以為是深拷貝的技巧 spread operator
1 | const A = {num: 1} |
看似沒問題,但如果改成二階以上的物件的話…
1 | const A = {num: {ch: '一'}} |
另外還有一個做法是 Object.assign()
但結果跟解構賦值一樣
僅限於一階物件
深拷貝
我個人最喜歡的作法是用JSON.stringify
和JSON.parse
直接把物件文字化再物件化一次
就一定會是另一個物件了
達到深拷貝的效果,像這樣:
const B = JSON.parse(JSON.stringify(A))
或是如果嫌每次都這樣寫麻煩
可以在專案中定義一個 deepClone function
1 | const deepClone = (obj) => JSON.parse(JSON.stringify(obj)) |
結論
淺拷貝: 看似複製,其實是指向相同位址
深拷貝: 另用一個記憶體存值,實質意義上的複製
