JS中的淺拷貝和深拷貝


Posted by Andy Tsai on 2020-07-19

淺拷貝(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 傳值、傳址、淺拷貝、深拷貝


#深拷貝 #淺拷貝 #javascript #Shallow Copy #Deep Copy #w3HexSchool







Related Posts

[ week 2 ]  打造 JaveScript 的基礎 - 基本運算 &位元運算

[ week 2 ] 打造 JaveScript 的基礎 - 基本運算 &位元運算

[ 筆記 ] JavaScript 進階 08 - new、extends、super、封裝

[ 筆記 ] JavaScript 進階 08 - new、extends、super、封裝

[MTR04] W2 D12 練習三

[MTR04] W2 D12 練習三


Comments