JS中的淺拷貝和深拷貝
淺拷貝(Shallow Copy)
先來看看這段 code
let obj1 = {name: 'paul', age: 34}
let obj2 = obj1
obj1.age = 18
console.log(obj2.age) //18我們建立一個變量 obj2,然後直接把 obj1 複製過去,再將 obj1 的 age 改為 18,看起來沒什麼問題,但是當 console.log(obj2.age)時會發現,我們明明只改了 obj1,但是 obj2 的 age 也一起被改了
這是因為在 JS 中物件操作是 call by reference,所以 obj1 和 obj2 它們其實是共用一個記憶體,因此當一方值變了,另一方也會跟著變(它們指向同一個實體),這就是淺拷貝。
** 補充:傳值(call by value)和傳址(call by reference) **
Javascript 中基本型別是傳值,物件型別是傳址
// 基本型別
let a = 2;
let b = 2;
console.log(a === b) //true,值一樣就會相等
// 物件型別
let obj1 = { name: 'james'}
let obj2 = { name: 'james'}
let obj3 = obj1;
console.log(obj1 === obj2) //false
/* 雖然obj1和obj2的值相同,但這裡是傳址,
它們存在於不同的記憶體空間,是各別獨立的實體,所以並不相等 */
console.log(obj1 === obj3) //true
/* 將obj1直接複製給obj3,它們將共用同個記憶體空間,指向同一個實體,
因此兩者相等,也就是淺拷貝*/Object.assign()、Object spread
那如果用 Object.assign()或 Object spread 來複製呢? 我們就來實驗一下
let obj1 = {name: 'paul', age: 34}
let obj2 = Object.assign({}, obj1);
let obj3 = {...obj1}
obj1.age = 18
console.log(obj2.age) //34
console.log(obj3.age) //34
看起來沒什麼問題,obj2 和 obj3 的 age 都沒有隨著 obj1 被修改,
那再稍微變化一下
let obj1 = {name: 'paul', family: { mom: 'Carlin'}}
let obj2 = Object.assign({}, obj1)
let obj3 = {...obj1}
obj1.family.mom = 'Michelle'
console.log(obj2.family) // {mom: "Michelle"}
console.log(obj3.family) // {mom: "Michelle"}喔喔,破功了,又隨 obj1 修改了,
因為這種方式只能複製第一層,如果資料結構複雜一點,就會失效,所以它們依然是一種淺拷貝
深拷貝(Deep Copy)
淺拷貝是共用記憶體,會互相影響,而深拷貝就是各自存在不同記憶體,各自獨立,不會互相影響。

圖片來源:https://we-are.bookmyshow.com/understanding-deep-and-shallow-copy-in-javascript-13438bad941c
那麼,如何達成深拷貝呢?
常見的方法是用 JSON.stringify()和 JSON.parse(),將物件轉成字串 再轉成物件,它就會是一個新的物件了
let obj1 = {name: 'paul', age: 34}
let obj2 = JSON.parse(JSON.stringify(obj1));
obj1.age = 18
console.log(obj2.age) //34不過這種方法是有些不足的,當遇到 function、undefined、正則等等都會有問題,僅適合單純只有資料的物件
如果需要達成完善的深拷貝,直接用 loadash 中提供的 cloneDeep 是比較快的方法
** Reference: **
關於 JS 中的淺拷貝(shallow copy)以及深拷貝(deep copy)
JavaScript 傳值、傳址、淺拷貝、深拷貝
