又被雷到了。
簡述
這是最近在寫 React 時踩到的雷,本來還以為我對拷貝問題沒有太大的障礙才對,結果還是太大意惹,所以重新回來複習一遍。
情境
這邊用一個例子來舉例會比較好懂一點:
1 | const a = [{value: 10}] |
假設我想要多加一個變數 b
,他的值要是 [{value: 20}]
,那你會怎麼做?
當時的我是這樣做的:
1 | const a = [{value: 10}] |
如果你已經看出問題的話,幹得好!沒有的話也沒關係,我們一步一步來解析。
首先這個作法當然是錯的,你可以試著把 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 | const a = [{value: 10}] |
第二種:
1 | const a = [{value: 10}] |
第一種是利用 JSON 轉換的方式,第二種是先利用 map
遍歷 Object,每一圈都做淺拷貝(因為只有一層所以不會是 reference),再把要改的部分修正。
兩種方法其實都行,不過我是聽說第一種會有效能問題,所以還是都用第二種居多。