下面是一个完整的深拷贝实现,考虑了循环引用、Symbol、函数、Date、RegExp、Map、Set等特殊类型:
function deepClone(target, map = new WeakMap()) {
// 处理原始类型和函数(函数直接返回,因为通常不需要拷贝)
if (typeof target !== 'object' || target === null) {
return target;
}
// 处理循环引用
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);
}
// 处理数组和普通对象
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;
}
关键点解析
循环引用处理:
使用WeakMap来存储已拷贝的对象,遇到相同的引用时直接返回存储的副本
特殊对象处理:
Date:创建新的Date实例
RegExp:创建新的RegExp实例
Map/Set:递归拷贝其内容
Symbol:创建新的Symbol,保持相同的description
函数处理:
函数直接返回原函数,因为通常不需要拷贝函数
Symbol作为key:
使用Object.getOwnPropertySymbols()获取Symbol key并递归拷贝
性能考虑:
使用WeakMap而不是Map,避免内存泄漏
只在必要时创建新对象
使用示例
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])
};
// 创建循环引用
obj.self = obj;
const cloned = deepClone(obj);
console.log(cloned);