簡單去解釋的話,淺拷貝與深拷貝最大的差異在於 Global Object (以下簡稱 GO) 上的複製引用賦值與記憶體位址,原始值的依賴關係
而深淺拷貝所對應的資料操作型別僅限於 Array
, Object
這樣的複合型資料結構
其操作對象僅限於 Array
, Object
,同時賦值引用狀況不同,其深淺拷貝的狀態也不同
Ex:
const obj1 = { name: "string", value: 2, nested: { a: 4 } };
const obj2 = obj1;
const obj2.name = "newString"
// obj1 = { name: "newString", value: 2, nested: { a: 4 } };
當如同 obj2 一般的引用時,就都屬於淺拷貝 只有資料是巢狀型態時深拷貝,但 nested 由於是巢狀資料,nested 的下一層會是引用 obj1 的記憶體位址,此時上一層的 name, value 在記憶體位址上是新的位址,並不是引用於 obj1
const foo = { name: "stirng", value: 123 };
const bar = Object.assgin({}, foo);
const bar_1 = { ...foo };
上述皆屬於淺拷貝的範疇,在 GO 的運作機制上 foo
, bar
, bar_1
的記憶體位址是共同持有的,bar
, bar_1
是引用 foo
,但三者任一發生了資料更新變動時,所有 variable 被訪問的值都會是被重新賦值後的 value
Ex:
const foo = { name: "stirng", value: 123 };
const bar = Object.assgin({}, foo);
const bar_1 = { ...foo };
bar.value = 444;
console.log(foo.value); // 444
console.log(bar.value); // 444
console.log(bar_1.value); // 444
cosnt a = [1,2,3,4]
const b = a
a.push(5)
console.log(a) // [1, 2, 3, 4, 5]
console.log(b) // [1, 2, 3, 4, 5]
對應 React, Vue 等現代框架的 Data immutable
理念來說,就是深拷貝的一種大量封裝應用方式,原因在於現代框架利用 vDom 的特性做前後比對,針對變動資料單獨渲染,以降低實際渲染上的時間損耗,提升使用體用
而這些與深拷貝的特性不謀而合,不論如何進行資料操作,其本身都是在 GO 開闢一塊新的記憶體位址儲存操作後的資料。原始資料並不會發生任何改動,直到 JS Runtime 該值的沒有其他引用後進行回收銷毀。
但有些狀況深拷貝的資料結構可能會發生改變,例如:
const obj1 = { name: "string", value: 2, nested: { a: 4 } };
const obj2 = { ...obj1 };
obj2.nested.a = 5;
// obj1 = { name: "string", value: 2, nested: { a: 5 } };
// obj2 = { name: "string", value: 2, nested: { a: 5 } };
當使用 Spread Syntax 來解構並賦值時,資料若是巢狀那麼深拷貝僅會執行在 Spread 所訪問的資料層級,向下的巢狀結構將會用淺拷貝來執行,此時若是直接改動 nested
就會連帶改動到 obj1 的 nested 值
PS: 當 nested
這個 key value 賦值後的資料型態是複合型別時 (Array
, Object
) 賦值特性仍是淺拷貝,但重新賦值的資料型別屬於Array
, Object
時,就會成深拷貝的特性,這樣的賦值將不會去更動到 obj1 的原始資料