深拷贝 map 和 set 必须用 foreach 或 entries() 提取键值/元素并递归克隆,不能依赖 for...in;需用 weakmap 缓存处理循环引用;structuredclone 可原生支持但不兼容函数、symbol 等。

JavaScript 中深拷贝 Map 和 Set 不能靠简单递归遍历对象属性,因为它们的键值对不通过 for...in 或 Object.keys() 暴露,也不在原型链上。必须用各自实例的专用方法(如 forEach、entries())提取数据,并新建对应类型实例进行重建。
Map 的深拷贝:逐对克隆键与值
Map 的每个键和值都可能是任意类型(包括对象、Symbol、甚至另一个 Map),所以必须对键和值分别递归深拷贝:
- 创建新
Map实例 - 调用
originalMap.forEach((value, key) => {...})或originalMap.entries() - 对每个
key和value单独调用深拷贝函数 - 用
newMap.set(clonedKey, clonedValue)写入新 Map
示例片段:
if (obj instanceof Map) {
const copy = new Map();
obj.forEach((value, key) => {
copy.set(deepClone(key, cache), deepClone(value, cache));
});
return copy;
}
Set 的深拷贝:逐项递归复制元素
Set 是无序唯一集合,没有键值对概念,只需遍历其所有元素并深拷贝后加入新 Set:
- 创建新
Set实例 - 用
originalSet.forEach(item => {...})或展开运算符[...originalSet] - 对每个
item执行深拷贝 - 用
newSet.add(clonedItem)添加
注意:若 Set 元素是对象或嵌套结构,不深拷贝会导致新 Set 仍引用原对象。
if (obj instanceof Set) {
const copy = new Set();
obj.forEach(item => copy.add(deepClone(item, cache)));
return copy;
}
别忘了缓存与循环引用
Map 和 Set 本身可能参与循环引用(比如 Map 的某个 value 是它自己,或 Set 包含自身):
- 必须在进入拷贝前检查
cache.has(obj) - 首次遇到时立即
cache.set(obj, copy),再填充内容 - 否则递归调用会再次触发拷贝逻辑,导致栈溢出或重复构造
WeakMap 仍是推荐缓存结构,因键为对象引用,且不阻止垃圾回收。
structuredClone 能直接处理吗?
现代环境(Chrome 98+、Firefox 94+、Node.js 17.0+)中,structuredClone(obj) 原生支持 Map 和 Set,自动递归深拷贝其全部内容,也天然处理循环引用。但它有明确限制:
- 不支持函数、
undefined、Symbol作为键或值(会丢失或报错) - 不保留自定义类的原型,只还原内置行为(如 Map.prototype.get 仍可用,但自定义方法丢失)
- 若项目需兼容旧浏览器或处理 Symbol 键,仍需手写逻辑
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











