再來談談-淺拷貝與深拷貝的雷

又被雷到了。

簡述

這是最近在寫 React 時踩到的雷,本來還以為我對拷貝問題沒有太大的障礙才對,結果還是太大意惹,所以重新回來複習一遍。

情境

這邊用一個例子來舉例會比較好懂一點:

1
const a = [{value: 10}]

假設我想要多加一個變數 b,他的值要是 [{value: 20}],那你會怎麼做?

當時的我是這樣做的:

1
2
3
const a = [{value: 10}]
const b = [...a]
b[0].value = 20

如果你已經看出問題的話,幹得好!沒有的話也沒關係,我們一步一步來解析。

首先這個作法當然是錯的,你可以試著把 a 跟 b 印出來看就會發現兩個都會是 [{value: 20}],但重點是為什麼會這樣?

如果你要看比較詳細的解說,可以參考這篇 該來談談淺拷貝(Shallow copy)與深拷貝(Deep copy)了,這邊只會大概講一下原因。

簡單來說,當我用 [...a] 的時候,你可以把我複製出來的東西想成是這樣:

1
const b = [reference]

所以如果你這時候用 b[0] === a[0],答案就會是 true,因為他們是同個 reference。這也就是為什麼 b[0].value = 20 也會改到 a 的原因。

雖然現在講的很有把握,不過當下還真的蠻疑惑為什麼會有這樣的結果。

正確的做法

總而言之,正確的做法就是避免掉淺拷貝帶來的問題,所以有兩種方式,先來看第一種:

1
2
3
4
5
6
const a = [{value: 10}]
const b = JSON.parse(JSON.stringify(a))
b[0].value = 20

console.log(a) // [{value: 10}]
console.log(b) // [{value: 20}]

第二種:

1
2
3
4
5
6
7
const a = [{value: 10}]
const b = a.map(item => {
return {...item, value: 20}
})

console.log(a)
console.log(b)

第一種是利用 JSON 轉換的方式,第二種是先利用 map 遍歷 Object,每一圈都做淺拷貝(因為只有一層所以不會是 reference),再把要改的部分修正。

兩種方法其實都行,不過我是聽說第一種會有效能問題,所以還是都用第二種居多。

React-CSS Module React-這個 fetch 我剛剛要但現在又不要了
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×