文章中英模式
布魯斯前端JS面試題目 - 深拷貝與淺拷貝深入解析
深入解析 JavaScript 中的深拷貝與淺拷貝機制,包含實作原理、效能考量、React 應用場景,以及面試常見問題的完整解答。
文章中英模式
懶得看文章?那就來看影片吧
基本概念
在 JavaScript 中,變數賦值時會根據資料型別採用不同的複製方式:
- •
基本型別
:直接複製值(number、string、boolean、null、undefined、symbol) - •
參考型別
:複製參考位址(object、array、function)
淺拷貝 (Shallow Copy)
只複製第一層的值,巢狀物件仍然共享參考位址。常見方法:
// 1. Object.assign()
const original = { name: 'Bruce', info: { age: 25 } };
const copy1 = Object.assign({}, original);
// 2. 展開運算子
const copy2 = { ...original };
// 3. Array 方法
const arr = [1, [2, 3]];
const arrCopy1 = Array.from(arr);
const arrCopy2 = arr.slice();
深拷貝 (Deep Copy)
遞迴複製所有層級的值,創建完全獨立的新物件。常見方法:
// 1. structuredClone (推薦,但需注意瀏覽器支援)
const deepCopy1 = structuredClone(original);
// 2. JSON 方法 (有限制)
const deepCopy2 = JSON.parse(JSON.stringify(original));
// 3. 使用 Lodash 函式庫
// 需先安裝: npm install lodash
import _ from 'lodash';
const deepCopy3 = _.cloneDeep(original);
// 4. 遞迴實現
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj;
const copy = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
copy[key] = deepClone(obj[key]);
}
}
return copy;
}
🔥 常見面試題目
(一)淺拷貝和深拷貝的區別?
解答:
const original = {
name: 'Bruce',
info: { age: 25 }
};
// 淺拷貝
const shallow = { ...original };
shallow.info.age = 26;
console.log(original.info.age); // 26 - 原物件被修改
// 深拷貝
const deep = structuredClone(original);
deep.info.age = 27;
console.log(original.info.age); // 26 - 原物件不受影響
(二)為什麼不建議使用 JSON.parse(JSON.stringify()) 進行深拷貝?
解答:JSON 方法有以下限制:
const obj = {
func: () => console.log('Hello'),
symbol: Symbol('test'),
undefined: undefined,
date: new Date(),
regexp: /test/,
[Symbol('key')]: 'value'
};
const copy = JSON.parse(JSON.stringify(obj));
console.log(copy.func); // undefined - 函式丟失
console.log(copy.symbol); // undefined - Symbol 丟失
console.log(copy.undefined); // undefined - undefined 丟失
console.log(copy.date); // 字串 - 不再是 Date 物件
console.log(copy.regexp); // {} - 正則表達式變成空物件
console.log(copy[Symbol('key')]); // undefined - Symbol key 丟失
(三)如何實現一個深拷貝函式?
解答:
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') return obj;
const copy = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
copy[key] = deepClone(obj[key]);
}
}
return copy;
}
function deepClone2(value, hash = new WeakMap()) {
// Handle null and non-objects
if (value === null || typeof value !== 'object') return value;
// Handle Date objects
if (value instanceof Date) return new Date(value);
// Handle RegExp objects
if (value instanceof RegExp) return new RegExp(value);
// Handle circular references
if (hash.has(value)) return hash.get(value);
// Create new array or object
const copy = Array.isArray(value) ? [] : {};
// Record processed objects to avoid circular references
hash.set(value, copy);
// Recursively process all properties
Object.entries(value).forEach(([key, val]) => {
copy[key] = deepClone(val, hash);
});
return copy;
}
// Usage example
const obj = {
array: [1, { a: 2 }],
date: new Date(),
regexp: /test/,
func: function() {},
nested: {
deep: { value: 123 }
}
};
// Create circular reference
obj.self = obj;
const cloned = deepClone(obj);
console.log(cloned.nested.deep.value); // 123
console.log(cloned.self === cloned); // true - maintains circular reference
console.log(cloned.self !== obj); // true - is a new object