手写深拷贝Set/Map需先用instanceof判断类型,再递归克隆元素或键值对,并用WeakMap处理循环引用;支持Symbol键、undefined值,函数值通常复用引用。

识别 Set 和 Map 类型并单独构造新实例
手写深拷贝时,不能把 Set 或 Map 当作普通对象遍历。必须先用 Array.isArray()、Object.prototype.toString.call() 或更准确的 value instanceof Set / value instanceof Map 判断类型,再分别处理。
关键点是:Set 和 Map 的键值可能包含任意类型(包括对象、嵌套 Set/Map、甚至函数),所以不能只复制其 entries() 数组,而要递归克隆每个键和每个值。
- 对 Set:遍历 set.values() 或用 for...of,逐个克隆元素后 add() 到新 Set 中
- 对 Map:用 map.entries() 获取 [key, value] 对,对每一对的 key 和 value 都递归深拷贝,再 set() 到新 Map
- 注意 Map 的键可能是对象或 Set —— 这些本身也要被深拷贝,否则新 Map 的键仍指向原引用
处理嵌套结构与循环引用
Set 和 Map 内部可能嵌套其他 Set/Map,甚至相互引用(比如一个 Map 的值是某个 Set,该 Set 又包含这个 Map 的某个键)。此时若不检测循环引用,递归会无限进行。
推荐用 WeakMap 缓存已克隆过的源对象及其对应的新对象:
立即学习“Java免费学习笔记(深入)”;
- 每次进入拷贝前,先查 WeakMap 是否已有该对象的克隆结果;有则直接返回,避免重复和死循环
- 在创建新 Set/Map 实例后、填充内容前,就将其存入 WeakMap,绑定原始对象
- WeakMap 适合这里,因为它只接受对象为键,且不影响垃圾回收,不会造成内存泄漏
兼容不可枚举键、Symbol 键与特殊值
原生 structuredClone 不支持 Symbol 键和函数键,手写实现可以按需选择是否保留:
- 若需支持 Symbol 键的 Map,不能用 Object.keys() 或 for...in,必须用 map.entries() —— 它能正确返回 Symbol 键
- undefined 值在 Set/Map 中合法,但 JSON 方式会丢失;手写时应原样保留,并在递归中正确传递
- 遇到函数作为值(非键),通常选择“引用复用”(即不深拷贝,直接赋原引用),因为 JS 中无法真正复制函数逻辑
示例核心逻辑片段
以下不是完整函数,而是处理 Set/Map 的关键骨架:
const clone = (value, seen = new WeakMap()) => {
if (value === null || typeof value !== 'object') return value;
if (seen.has(value)) return seen.get(value);
if (value instanceof Set) {
const cloned = new Set();
seen.set(value, cloned);
for (const item of value) cloned.add(clone(item, seen));
return cloned;
}
if (value instanceof Map) {
const cloned = new Map();
seen.set(value, cloned);
for (const [key, val] of value.entries()) {
cloned.set(clone(key, seen), clone(val, seen));
}
return cloned;
}
// 其他类型:Date、RegExp、普通对象、数组……
};


















