
關於Javascript中的Shallow Copy(淺拷貝)及Deep Copy(深拷貝)
關於Javascript中的Shallow Copy(淺拷貝)及Deep Copy(深拷貝), 前幾天在群組看到有人在討論Shallow copy 及 Deep copy,上網查了些資料發現收穫良多,對JS又有更進一步的認識了~這邊做個筆記紀錄一下。
型別介紹
在Javascript裡面,有分為原始型別(Primitive Type)及物件(Non-Primitive)(MDN)。
原始型別有
- Number
- String
- Boolean
- Null
- Undefined
- Symbol(ES6)
物件
- Object
- Array(可參考)
- …
原始型別在賦值時一般是直接傳值的方式(pass by value),如
let a = 5;
let b = a;
b = 10
console.log(a) //5
console.log(b) //10
但是物件會是傳指標(pass by Reference),如
let obj1 = { a:5, b:10, c:15};
let obj2 = obj1;
obj2.b = 20
console.log(obj1) //{ a:5, b:20, c:15} 改b卻影響a,因為是傳Reference
console.log(obj2) //{ a:5, b:20, c:15}
所以一般我們在[複製]物件(or 陣列)時會利用函式處理,而不會直接用等號賦值。但是在複製時,分為淺拷貝及深拷貝。
淺拷貝(Shallow Copy) VS 深拷貝 (Deep Copy)
可以參考這張圖
對Shallow copy來說,只是複製collection structure,而不是element。所以在指派第二層物件時會出現問題。
而Deep Copy是整個複製,包含element。所以當我們在使用多層物件時,要盡量用deep copy。
上網看到有人用英文解釋得很好
Shallow copies duplicate as little as possible. A shallow copy of a collection is a copy of the collection structure, not the elements. With a shallow copy, two collections now share the individual elements.
Deep copies duplicate everything. A deep copy of a collection is two collections with all of the elements in the original collection duplicated.
Collections can be diverse data structures which stores multiple data items.
所以說,對於淺拷貝而言,第一層的原始型別是傳值,而物件的話則是傳遞REFERENCE。
let obj1 = {a:{b:5}};
let obj2 = {a:obj1.a};//都把a指派到{b:5}這個物件的reference
obj2.a.b = 10
console.log(obj1) //{a:{b:10}}; 被指派到10
console.log(obj2) //{a:{b:10}};
console.log(obj1 === obj2) //false 實際上不是同物件
console.log(obj1.a === obj2.a) //true 但是第二層物件實際上相同
所以當我們要確保物件也是整個複製,而不只只是複製reference時就需要用到Deep Copy
而一般我們在使用JS時,有哪些是屬於Shallow Copy,又有哪些是屬於Deep Copy呢?
Shallow Copy
Array.concat
:一般Array.concat
的用法是合併兩個陣列,如var alpha = ['a', 'b', 'c'], numeric = [1, 2, 3]; var alphaNumeric = alpha.concat(numeric); console.log(alphaNumeric); // 結果: ['a', 'b', 'c', 1, 2, 3]
Array.slice
一般Array.slice()
的方法是複製一個新的陣列,帶入的參數為Array.slice(start, end)
,當不輸入參數值哲是直接複製一個。var animals = ['ant', 'bison', 'camel', 'duck', 'elephant']; console.log(animals.slice(2)); // expected output: Array ["camel", "duck", "elephant"] console.log(animals.slice(2, 4)); // expected output: Array ["camel", "duck"] console.log(animals.slice()); //expected output: Array ['ant', 'bison', 'camel', 'duck', 'elephant'];
- Object .assignObject.assign用來合併物件,用法為
Object.assign(target, ...source)
若目標物件為空物件則可視為複製一個source
的物件。var obj = { a: 1 }; var copy = Object.assign({}, obj); console.log(copy); // { a: 1 }
Deep Copy
- JSON.parse(JSON.stringify(object_array))一次用到兩個函式:
- JSON.parse():把字串轉成物件
- JSON.stringify():把物件轉成字串。
let obj1 = {a:{b:10}}; let obj2_string = JSON.stringify(obj1); console.log(obj2_string); //"{"a":{"b":10}}"; let obj2 = JSON.parse(obj2_string); console.log(obj2); //{a:{b:10}} obj2.a.b = 20; console.log(obj1); //{a:{b:10}} console.log(obj2); //{a:{b:20}}
- Jquery的
extend
// Shallow copy var objectIsNew = jQuery.extend({}, objectIsOld); // Deep copy var objectIsNew = jQuery.extend(true, {}, objectIsOld);
- Lodash的
_.cloneDeep
var objects = [{ 'a': 1 }, { 'b': 2 }]; var deep = _.cloneDeep(objects); console.log(deep[0] === objects[0]); // => false
相信經過以上的介紹之後,大家對於JS的pass by value 及 pass by reference更為熟悉了吧!