在 JavaScript 中,淺複製和深複製都用於複製物件的內容,但它們在複製的方式和效果上有顯著的區別。理解它們的差異對於避免常見的 bug 和正確使用資料結構非常重要。
1. 淺複製(Shallow Copy)
淺複製是指建立一個新的物件,但新的物件中僅複製原始物件的第一層屬性(即原始物件的屬性值)。如果屬性值本身是一個物件或陣列,那麼它不會被複制,而是會保留對原始物件屬性的引用。
特點:
- 淺複製只複製物件的“第一層”屬性,對於屬性值是物件或陣列的情況,它們仍然指向原始物件的引用。
- 這樣修改複製物件中的巢狀物件會影響到原始物件中的巢狀物件。
例子:
const obj = { a: 1, b: { c: 2 } };
const shallowCopy = { ...obj }; // 使用擴充套件運算子進行淺複製
shallowCopy.a = 10; // 修改第一層的屬性
shallowCopy.b.c = 20; // 修改巢狀物件中的屬性
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(shallowCopy); // { a: 10, b: { c: 20 } }
解釋:
shallowCopy.a = 10
只改變了第一層屬性a
的值,原始物件obj
中的a
仍然是1
。shallowCopy.b.c = 20
影響了b
這個巢狀物件,因為它們仍然引用了同一個物件。
常見的淺複製方法:
- 使用
Object.assign()
:const shallowCopy = Object.assign({}, obj);
- 使用擴充套件運算子(
...
):const shallowCopy = { ...obj };
2. 深複製(Deep Copy)
深複製是指建立一個新的物件,並且遞迴地複製原始物件的所有屬性,不管這些屬性的值是原始型別(如字串、數字、布林值等),還是引用型別(如物件、陣列等)。深複製會完全獨立於原始物件,因此修改新物件中的屬性不會影響原始物件。
特點:
- 深複製會複製物件的所有層級,包括巢狀的物件或陣列,確保沒有任何引用關係。
- 修改深複製後的物件,不會影響原始物件。
例子:
const obj = { a: 1, b: { c: 2 } };
const deepCopy = JSON.parse(JSON.stringify(obj)); // 使用 JSON 方法進行深複製
deepCopy.a = 10;
deepCopy.b.c = 20;
console.log(obj); // { a: 1, b: { c: 2 } }
console.log(deepCopy); // { a: 10, b: { c: 20 } }
解釋:
deepCopy
是透過JSON.parse(JSON.stringify())
實現的深複製,這種方法可以確保obj
中的每一層都被複制,並且是完全獨立的。- 修改
deepCopy
中的屬性a
和b.c
不會影響obj
,因為它們不再共享引用。
常見的深複製方法:
- 使用
JSON.parse(JSON.stringify(obj))
(但有侷限性,不能複製函式、undefined
、Symbol
等,且無法處理迴圈引用)。 - 使用遞迴方法手動實現深複製。
- 使用第三方庫(如
Lodash
的cloneDeep
方法)。
// 使用 Lodash 的 deep clone 方法
const _ = require('lodash');
const deepCopy = _.cloneDeep(obj);
3. 淺複製與深複製的區別總結
特性 | 淺複製 | 深複製 |
---|---|---|
複製深度 | 僅複製一層(第一層)屬性 | 遞迴複製所有層級的屬性 |
對引用型別的處理 | 僅複製引用型別的引用 | 複製引用型別的內容,完全獨立 |
修改結果 | 修改巢狀物件會影響原始物件 | 修改複製物件不會影響原始物件 |
常見方法 | Object.assign() 、擴充套件運算子(... ) |
JSON.parse(JSON.stringify()) 、手動遞迴、Lodash.cloneDeep() |
4. 淺複製和深複製的應用場景
-
淺複製:
- 用於複製物件的第一層屬性,但不需要複製巢狀物件。適用於僅需要修改一層資料的場景。
- 在元件狀態管理中,如果物件的巢狀屬性沒有被修改,淺複製可以避免不必要的深度複製,提高效能。
-
深複製:
- 用於確保物件之間完全獨立,特別是當物件中包含巢狀的引用型別(如陣列或物件),且這些巢狀物件需要獨立修改時。
- 在需要避免對原始資料進行修改(如在狀態管理中,或者操作不可變資料時)非常有用。
總結
- 淺複製適用於只需要複製物件的第一層屬性的場景,且不會影響引用型別的修改。
- 深複製適用於需要確保完全獨立的物件副本,尤其是在物件中包含巢狀引用型別時。
根據不同的應用場景選擇適當的複製方式,能有效提高程式碼的效率和可靠性。