下面是一個(gè)完整的深拷貝實(shí)現(xiàn),考慮了循環(huán)引用、Symbol、函數(shù)、Date、RegExp、Map、Set等特殊類型:
function deepClone(target, map = new WeakMap()) {
// 處理原始類型和函數(shù)(函數(shù)直接返回,因?yàn)橥ǔ2恍枰截悾? if (typeof target !== 'object' || target === null) {
return target;
}
// 處理循環(huán)引用
if (map.has(target)) {
return map.get(target);
}
// 處理Date對象
if (target instanceof Date) {
return new Date(target);
}
// 處理RegExp對象
if (target instanceof RegExp) {
return new RegExp(target);
}
// 處理Map
if (target instanceof Map) {
const clone = new Map();
map.set(target, clone);
target.forEach((value, key) => {
clone.set(deepClone(key, map), deepClone(value, map));
});
return clone;
}
// 處理Set
if (target instanceof Set) {
const clone = new Set();
map.set(target, clone);
target.forEach(value => {
clone.add(deepClone(value, map));
});
return clone;
}
// 處理Symbol
if (typeof target === 'symbol') {
return Symbol(target.description);
}
// 處理數(shù)組和普通對象
const clone = Array.isArray(target) ? [] : {};
map.set(target, clone);
// 處理Symbol作為key的情況
const symKeys = Object.getOwnPropertySymbols(target);
if (symKeys.length) {
symKeys.forEach(symKey => {
clone[symKey] = deepClone(target[symKey], map);
});
}
// 處理普通key
for (const key in target) {
if (target.hasOwnProperty(key)) {
clone[key] = deepClone(target[key], map);
}
}
return clone;
}
關(guān)鍵點(diǎn)解析
循環(huán)引用處理:
使用WeakMap來存儲已拷貝的對象,遇到相同的引用時(shí)直接返回存儲的副本
特殊對象處理:
Date:創(chuàng)建新的Date實(shí)例
RegExp:創(chuàng)建新的RegExp實(shí)例
Map/Set:遞歸拷貝其內(nèi)容
Symbol:創(chuàng)建新的Symbol,保持相同的description
函數(shù)處理:
函數(shù)直接返回原函數(shù),因?yàn)橥ǔ2恍枰截惡瘮?shù)
Symbol作為key:
使用Object.getOwnPropertySymbols()獲取Symbol key并遞歸拷貝
性能考慮:
使用WeakMap而不是Map,避免內(nèi)存泄漏
只在必要時(shí)創(chuàng)建新對象
使用示例
const obj = {
num: 1,
str: 'hello',
arr: [1, 2, 3],
date: new Date(),
reg: /abc/gi,
fn: function() { console.log('function') },
[Symbol('sym')]: 'symbol value',
map: new Map([['key', 'value']]),
set: new Set([1, 2, 3])
};
// 創(chuàng)建循環(huán)引用
obj.self = obj;
const cloned = deepClone(obj);
console.log(cloned);