淺拷貝(Shallow Copy)&深拷貝(Deep Copy)
在 Javascript 裡面,分為原始型別(Primitive Type)及物件(Non-Primitive)(MDN)。
原始型別有:
- Number
- String
- Boolean
- Null
- Undefined
- Symbol(ES6)
物件有:
- Object
- Array(可參考)
- Function
- Date
- Regx(正規)
- Construct(建構式)
原始型別在賦值時一般是直接傳值(pass by value),如
1 | let a = 1; |
物件會是傳參考(pass by Reference,變數的記憶體位址),如
1 | let a = { |
為了達成修改 a 物件底下的屬性時,而不會影響到 b 物件底下的屬性值的這件事。所以一般我們在複製物件(or 陣列)時會利用函式
來處理,而不會直接用=
賦值。
而複製又分為淺拷貝(Shallow Copy)
&深拷貝(Deep Copy)
。
淺拷貝:
就是物件指向某個指標(Node,只有一層),但還是共用同一塊記憶體。
深拷貝:
就是另外建立新的物件,有同樣的內容,但不共用記憶體位址。
淺拷貝(Shallow Copy)
物件只有一層時使用,像以下兩種情況。
arr = [1,2,3]
or
1 | let a = { |
陣列
可用 slice(0)來做處理。
Array.concat()也是淺拷貝。
e.g.
1 | let arr1 = [1,2,3]; |
物件
ES6 有 Object.assign()
的方法就可以進行淺拷貝。
Object.assign():會將一個或多個物件進行合併,並回傳一個 全新的物件參考
e.g.
1 | let a = { |
深拷貝(Deep Copy)
物件有多層時使用。利用JSON.stringify()
先把整個物件變成字串,再藉由JSON.parse()
轉回來物件的型態,此方法可以完全複製整個原型鏈達到深拷貝(Deep Copy)。
陣列
若是陣列裡的「元素」是「物件型別」的陣列。使用slice(0)
的方式。並沒有辦法避免互相影響。
e.g.
1 | let arr1 = [[1,2,3],[4,5,6]]; |
此時改用深拷貝。
e.g.
1 | function DeepClone(obj) { |
達成不互相影響的要求。
物件
同樣先用Object.assign()
來試試看。
e.g.
1 | let a = { |
改用JSON.parse
跟JSON.stringify
的方式來做。
e.g.
1 | let a = { |
另外 jQuery 中的 $.extend
方法,與 lodash _.cloneDeep
方法,也都可以用來實作深拷貝(Deep Copy)。
補充:
在 JavaScript 中{} === {} & {} == {} // return false
不相等的原因是,這兩個物件實字存放於兩個不同的記憶體空間位置。
實字與建構式
應該盡量避開建構式來建立物件或陣列,實字模式更加簡潔、更不易出錯。
物件實字:用
{}
建立
e.g.let car = {Brand:"Toyota"}
物件建構式:
new object()
e.g.let car = new object();
car.Brand = "Toyota";
陣列實字:用
[]
建立
e.g.let arr = [1,2,3];
陣列建構式:
new Array()
e.g.let arr = new Array(1,2,3);
參考:
https://dustinhsiao21.com/2018/01/07/javascript-shallow-copy-and-deep-copy/
http://skyroxas.tw/javascript-實作技巧:-淺拷貝shallow-copy-深拷貝deep-copy/
https://joansay.blogspot.com/2017/12/javascript_14.html
https://ithelp.ithome.com.tw/articles/10221481?sc=rss.iron
https://medium.com/schaoss-blog/前端三十-14-js-深拷貝是什麼-如何實現-a31841fccc9b