淺拷貝(Shallow Copy) VS 深拷貝(Deep Copy)
基本與物件型別傳值的差異
JavaScript 內建的型別主要可以分成基本型別 (Primitives) 與物件型別 (Object) 兩大類。
而基本型別又分成 string、number、boolean、null、undefined 幾種,
除了以上幾種之外,其他都可以歸類至物件型別 (Object)
這二種型別之間的差異,就是在他們的傳值方式
- 基本型別 => 傳值(value)
- 物件型別 => 傳址(reference)
下面範例來解說他們的差異
基本型別
基本型別是傳 value
1 | let a = "爸爸"; |
在修改 b 時並不會改到 a 的值
物件型別
但物件就不同,物件傳的是 reference
1 | let objA = { name: '王大頭' } |
淺拷貝(Shallow Copy) VS 深拷貝(Deep Copy)
淺拷貝:
只能達到淺層的複製(第一層),若有第二層以上的資料的話,
就無法達到實際的複製,而是會與舊物件一起共用同一塊記憶體。深拷貝:
會另外創造一個一模一樣的物件,新物件跟原物件不共用記憶體,修改新物件不會改到原物件。
Object.assign
Object.assign 是 ES6 的新函式,我們可以用來達成複製的功能
1 | let obj = {name: '王康寶', age:{child: 18}} |
But Object.assign 並不是那麼的完美,請再看下例
1 | let obj = {name: '王康寶', age:{child: 18}} |
我們可以看到更改 copy.age.child 值以後,發現 obj.age.child 值也跟著被變掉了,
所以 Object.assign 能處理深度,只有一層的物件,沒辦法做到真正的 深拷貝(Deep Copy),
不過如果要複製的物件只有一層的話可以考慮使用他。
另外也可以使用 展開運算子(Spread Operator) 達成複製,不過一樣是 淺層的複製。
1 | let obj = {name: '王康寶', age:{child: 18}} |
深拷貝的作法
jQuery
jquery 有提供一個 $.extend 可以用來做 Deep Copy
1 | let obj = {name: '王康寶', age:{child: 18}} |
lodash
lodash 也有提供 _.cloneDeep 用來做 Deep Copy
1 | let obj = {name: '王康寶', age:{child: 18}} |
參考來源
FB討論串
2018/04/26 社團剛好有人詢問此問題,也有大大回覆一些見解,留個紀錄可以回頭查。
簡單重點整理:
- 可用
JSON.parse(JSON.stringify({}))
偽深拷貝- 純資料,可行。
- 若遇 Function、Set、Map..等型態,失效。
- 為何要複製function? 目的?
- 站在節省記憶體的角度,function能重複利用就重複利用
- 若是要寫物件導向風格
- ES5:寫 Function + prototype
- ES6:寫 Class
JSON複製範例
1 | let a = {o:{v:1}} |