---
# System prepended metadata

title: 面試考題 2024 - 淺拷貝與深拷貝

---

面試考題 2024 - 淺拷貝與深拷貝（實作）
===

![image](https://hackmd.io/_uploads/ryWGwvg70.png)


---
###### tags: `面試問題`, `練習問題`, `2024`

其實製作這個系列是為了自己考試的時候可以被深入地刻在腦海裡，殊不知在專案裡都已經用過非常多次，但為了讓面試更容易敘述和讓面試官更了解自己的實力，也只好下點功夫來學習下正確的知識。

## 什麼是淺拷貝（Shallow Copy）
淺拷貝只會複製對象的第一層屬性。如果對象的屬性值基礎數據類型是 string啊，Number之類的，則會複製其值。
但如果是引用數據類型呢，例如 Array，Object，則會複製其引用而非實際本體。
注意：
**如果原始對象和拷貝對象的某個屬性都指向同一個對象時，修改這個對象將影響到兩者！**

## 淺拷貝的實現方式：
```jsx
let obj1 = { a: 1, b: { c: 2 } };
let obj2 = Object.assign({}, obj1);
obj2.a = 3;
obj2.b.c = 3; // 修改obj2中b属性的c属性，也会影响obj1
console.log(obj2);
```
![image](https://hackmd.io/_uploads/ryD3cul7C.png)

第二種：
```jsx
let obj1 = { a: 1, b: { c: 2 } };
let obj2 = { ...obj1 };
obj2.b.c = 3; // 修改obj2中b属性的c属性，也会影响obj1

```

以上這兩種充分地表現了淺拷貝的做法。
這裡會發現改變的對象是obj2可是obj1卻也跟著改變。
應驗了上面那句，如果原始對象和拷貝對象的某個屬性都指向同一個對象時，修改這個對象都會影響兩者。
 - 當你執行 **obj2.a = 3** 時，你是在修改 **obj2 的 a** 屬性的值。這裡的 **a** 是一個基本數據類型的屬性，所以這個操作僅僅修改了 **obj2 中的 a**， 並沒有影響 **obj1 的 a**
 - 當你執行 **obj2.b.c = 3** 時，你是在修改 **obj2 中的 b** 屬性指向對象中的 **c屬性**。因為 **b** 是一個引用數據類型的屬性，**obj1 和 obj2 中的 b 實際上指向同一個對象**，所以這個修改同樣影響了 **obj1 中的 b.c**


## 什麼是深拷貝（Deep Copy）
深拷貝複製的對象所有的層級，做法是創建一個完全獨立的副本。
深拷貝確保源對象和拷貝對象在內存中完全獨立，修改拷貝對象不會影響源對象。

簡單說，就是完全地，重新地成立一個新的空array/object, 然後把資料全部完全拷貝過去空的對象。

## 深拷貝的實作


```jsx
let obj1 = {a:1, b:{c:2}};
let obj2 = JSON.parse(JSON.stringify(obj1));
obj2.b.c =3 ; //這樣做不會影響 obj1
```
1. JSON.parse() 和 JSON.stringify():
 - 這是實現深拷貝的一個快捷方法，但它不會複製函數和循環引用的對象。



---


```jsx
function deepCopy(obj) {
    if(obj === null || typeof obj !== "object"){
        return obj;
    }
    let copiedObject = Array.isArray(obj)? []: {};
    for (let key in obj){
        if(obj.hasOwnProperty(key)){
            copiedObject[key] = deepCopy(obj[key]);
        }
    }
    return copiedObject;
}
```
2. 遞歸拷貝：
 - 可以自定義函數來遞歸遍歷和複製所有層級，這種方法可以處理複雜的情況，包含函數或循環引用的對象。

---
```jsx
import _ from 'lodash';
let obj1 = {a:1, b:{c:2}};
let obj2 = _.cloneDeep(obj1);
obj2.b.c = 3; //不影響obj1
```
3. 使用庫 （如 Lodash 的 ._cloneDeep()）
 - Lodash 提供了一個非常強大的深拷貝函數，可以處理各種類型的數據，包含循環引用等。


## 總結
簡單說呢，就是臨時的，接下來的操作需要拿出來組合用但又不會間接修改到原本結構的，可以用淺拷貝。
如果是需要重新組合成一個新的引用數據類，擔心會不小心搞到原本object對象的，可以考慮用深拷貝。
當然如果對象較為簡單或只需要拷貝頂層屬性而已，淺拷貝通常更快且消耗最少資源。

希望以上的解說能讓閱讀者學習到新的知識。加油，Peace
![image alt](https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExYXg4Znc1MDNtYnZydXE0aDNxNmh0dnhscjU3YzhpcmJjaHVwMjJjaiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/fxe8v45NNXFd4jdaNI/giphy.gif)
