文章中英模式
布魯斯前端JS面試題目 - 實作深拷貝函數
學習如何實作 JavaScript 深拷貝函數,處理複雜的嵌套物件與循環引用,掌握不同深拷貝方案的優缺點與實際應用。
文章中英模式
懶得看文章?那就來看影片吧
淺拷貝與深拷貝的區別
在 JavaScript 中,物件是引用類型,當我們將一個物件賦值給另一個變數時,實際上只是複製了對該物件的引用,而不是物件本身。這就是為什麼我們需要了解淺拷貝與深拷貝的區別。
淺拷貝 (Shallow Copy)
淺拷貝只複製物件的第一層屬性,對於嵌套的物件或陣列,仍然是複製其引用。
// 淺拷貝示例
const original = { a: 1, b: { c: 2 } };
// 使用 Object.assign 進行淺拷貝
const shallowCopy = Object.assign({}, original);
// 使用展開運算符進行淺拷貝
const spreadCopy = { ...original };
// 修改淺拷貝的嵌套屬性
shallowCopy.b.c = 3;
console.log(original.b.c); // 輸出: 3 (原物件也被修改了!)
console.log(spreadCopy.b.c); // 輸出: 3 (同樣被修改了!)
深拷貝 (Deep Copy)
深拷貝會遞迴地複製所有屬性,創建一個與原物件完全獨立的新物件,修改新物件不會影響原物件。
基本的深拷貝實作
下面是一個簡單的深拷貝函數實作,能夠處理基本的嵌套物件和陣列:
function deepClone(obj) {
// 處理基本類型和 null
if (obj === null || typeof obj !== 'object') {
return obj;
}
// 創建新的物件或陣列
const copy = Array.isArray(obj) ? [] : {};
// 遞迴複製所有屬性
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
copy[key] = deepClone(obj[key]);
}
}
return copy;
}
// 使用範例
const original = {
a: 1,
b: {
c: 2,
d: [3, 4, { e: 5 }]
}
};
const cloned = deepClone(original);
// 修改克隆對象的嵌套屬性
cloned.b.c = 10;
cloned.b.d[2].e = 20;
console.log(original.b.c); // 輸出: 2 (原始值保持不變)
console.log(original.b.d[2].e); // 輸出: 5 (原始值保持不變)
console.log(cloned.b.c); // 輸出: 10
console.log(cloned.b.d[2].e); // 輸出: 20
這個基本實作能處理普通物件和陣列,但有幾個限制:
- 1. 不能處理循環引用(會導致堆疊溢出)
- 2. 不能正確處理 Date、RegExp、Map、Set 等特殊物件
- 3. 不處理物件的原型鏈
- 4. 不處理不可枚舉屬性