# 淺層複製與深層複製
之前提到物件型別有傳參考的特性,但是如果想要另外修改資料,就會連帶影響原始資料內容,JavaScript 提供幾種複製方式來解決這個問題。
## 淺層複製 Shallow Copy
淺層複製只能複製物件的第一層,如果修改第二層資料的話,還是會影響原始資料。
### for-in loop
以下範例可以發現,用 `for-in` 迴圈複製後的新物件會和原始資料不同。
```javascript
var data = {
title: 'First Level',
info: {
tag: 'js'
}
}
var target = {}
for ( let key in data ) {
target[key] = data[key]
}
console.log(target !== data) // true
```
但是接著修改新物件後,修改第一層的內容沒有問題,不會影響原始資料,但是修改第二層後,就會連同原始物件一起修改。
```javascript
target.title = 'Second Level'
target.info.tag = 'copy'
console.log(target, data)
```

### Object.assign
`Object.assign()` 可以用來複製物件本身的屬性到另外一個目標物件上。
```javascript
var origin = { a: 1, b: 2, c: 3 }
var target = Object.assign({}, origin)
console.log(origin, target)
// origin = { a: 1, b: 2, c: 3 }
// target = { a: 1, b: 2, c: 3 }
```
如果 <font color="#ff7575">**目標物件**</font> 與 <font color="#6897bb">**原始物件**</font> 的屬性名稱相同,<font color="#6897bb">**原始物件**</font> 的屬性會覆蓋 <font color="#ff7575">**目標物件**</font>。
```javascript
var origin = { a: 1, b: 2, c: 3 }
var target = { a: 4, b: 5, x: 0 }
Object.assign(target, origin)
console.log(origin, target)
// origin = { a: 1, b: 2, c: 3 }
// target = {a: 1, b: 2, x: 0, c: 3}
```
### ES6 展開語法
除了以上兩種方法,也可以用 ES6 的展開語法達到相同效果。
```javascript
var data = { a: 1, b: 2, c: 3 }
var target = {...data}
target.a = 999
console.log(target)
// target = { a: 999, b: 2, c: 3 }
```
---
## 深層複製 Deep Copy
淺層複製只能複製第一層資料,深層複製則是可以將資料完整複製,讓整份新複製的資料都能往下層修改,並且不會影響原始資料。
### JSON.stringify
`JSON.stringify()` 可以將物件轉成「字串」後回傳。
重新轉成字串後再用 `JSON.parse()` 轉回 JavaScript 物件,如此就能達到深層複製的效果。
原理是將物件先轉成「純值」,再透過 `JSON.parse()` 轉回物件,此時的物件就會有一個新的記憶體空間,並且讓變數指向到新的位置。
<div class="alert alert-danger">
如果物件中有 <b>undefined、function</b>,因為無法轉成純值,所以會直接消失,<b>NaN 則會變成 Null</b>
</div>
```javascript
var data = {
title: 'First Level',
info: {
tag: 'js'
}
}
var target = JSON.parse(JSON.stringify(data))
target.title = 'Second Level'
target.info.tag = 'copy'
console.log(target, data)
```

### jQuery.extend
如果要解決 `undefined`、`function` 和 `NaN` 的複製問題,可以用 jQuery 的 `$.extend` 方法,就能做到完整的深層複製。
```javascript
var data = {
title: 'First Level',
info: {
tag: 'js'
},
func: function () {
console.log('data.func')
},
un: undefined,
nan: NaN
}
var shallow = $.extend({}, data) // 淺層複製
var deep = $.extend(true, {}, data) // 深層複製
console.log(deep)
```
---
## 參考資料
* [JavaScript 核心篇](https://www.hexschool.com/courses/js-core.html)
* [JavaScript 核心觀念(30)-物件-淺層複製及深層複製](https://hsiangfeng.github.io/javascript/20200905/1375484447/)