0%

JavaScript-淺層拷貝和深層拷貝

在JS的專案中很常會使用到物件,那物件就很容易會複製來複製去的,很容易會因為js的物件參照特性而有一些bug,那這一偏就來記錄一下,淺層拷貝和
深層拷貝的關係跟使用方法。
深層拷貝 代表新的變數的值 跟 原本的變數值 沒有任何關係。
淺層拷貝 代表新的變數的值 跟 原本的變數值還是有關係。

淺層拷貝

JS的物件操作是call by reference,所以,當我們直接將某個物件複製給另一個變數時,會因為這他們都指向同一個記憶體位置,集他們都共用同一個物件內容,
此種狀況即為淺拷貝。

1
2
3
4
let obj1 = { value:10 }
let obj2 = obj1
obj2.value = 20
console.log(obj1.value) // 20 被更改到了,淺層複製

深層拷貝

那我們可以用什麼方法達到深層複製呢? 使用Object.assign({}, oringinObj) 或者 使用 ES6的展開語法。
ex1: (Object.assign 用法)

1
2
3
4
let obj1 = { value:10 }
let obj2 = object.assign({}, obj1)
obj2.value = 20
console.log(obj1.value) // 10,因為深拷貝的關係,所以,彼此之間是沒有連結的

ex2: (ES6的展開語法)

1
2
3
4
5
6
7
8
let obj1 = {
value: 10,
}
let obj2 = {
...obj1
}
obj2.value = 100
console.log(obj1.value) // 10 ,還是維持原本的數值

Object.assign和ES6 展開語法的問題

但是,用這種深層拷貝的方法,在原物件第二層的成員屬性還是有淺層拷貝的問題。

1
2
3
4
5
6
7
8
9
10
11
let obj1 = {
value: 10,
address: {
road: 'revenue 1'
}
}
let obj2 = Object.assign({}, obj1)
obj2.value = 20
obj2.address.road = 'Road 2'
console.log(obj1.value) // 10 ,第一層就不會有影響,這就是深層拷貝
console.log(obj1.address.road) // Road 2 ,可以看到原物件的物件透過Object.assign的方式,還是有淺層拷貝的問題

消除深層拷貝的問題-用JSON轉型

1
2
3
4
5
6
7
8
9
10
11
let obj1 = {
value: 10,
address: {
road: 'Road 1'
}
}
let obj2 = JSON.parse(JSON.stringify(obj1))
obj2.value = 100
obj2.address.road = 'Road 2'
console.log(obj1.value) // 10 ,值都沒有改變
console.log(obj1.address.road) // Road 1 ,值都沒有改變

JSON轉型-深層拷貝的問題

雖然,上面的例子看起來JSON轉型有辦法解決深層拷貝的問題,但是,當原物件中的成員屬性值為NaNundefined,或者,成員屬性是function
JSON轉型的是沒辦法拷貝以上這些成員的,而NaN的值會被轉成null
那最終的辦法還是建議用框架,例如 jQuery 的 $.extend 就可以做到完整的深層拷貝。

1
2
3
4
5
6
7
8
9
let obj1 = {
value: 10,
fn: function() {
console.log(this.value)
},
un: NaN
}
let obj2 = $.extend(true, {}, obj1) // 用jQuery來深層拷貝解決以上的問題
console.log(obj2)

參考資料

  1. https://www.javascripttutorial.net/object/3-ways-to-copy-objects-in-javascript/
  2. https://medium.com/@yining1204/javascript-%E6%A0%B8%E5%BF%83%E7%AF%87-%E5%AD%B8%E7%BF%92%E7%AD%86%E8%A8%98-chap-38-%E6%B7%BA%E5%B1%A4%E8%A4%87%E8%A3%BD%E5%8F%8A%E6%B7%B1%E5%B1%A4%E8%A4%87%E8%A3%BD-c237fd383864
  3. https://hsiangfeng.github.io/javascript/20200905/1375484447/