深拷贝Map和Set应优先使用structuredClone(),它原生支持并自动处理嵌套结构与循环引用;手写需用Object.prototype.toString判断类型,对Map/Set新建实例并递归拷贝键值或元素,注意对象键通常复用引用。

JS 深拷贝要正确复制 Map 和 Set,不能靠 JSON.parse(JSON.stringify())(它会把它们变成空对象),也不能只用浅拷贝(会共享引用)。核心是识别类型、新建实例、递归处理键值或元素。
用 structuredClone() 直接支持(推荐)
这是现代浏览器和 Node.js(v17.0+)原生提供的方案,开箱即用:
-
structuredClone()会自动识别Map和Set,创建新实例并深拷贝所有键、值或成员 - 键和值如果是嵌套对象、其他
Map/Set、Date、RegExp等,也一并递归处理 - 天然支持循环引用,不会死循环或报错
示例:
const original = {
map: new Map([["a", { x: 1 }], ["b", [2, 3]]]),
set: new Set([{ y: 4 }, "hello"])
};
const cloned = structuredClone(original);
cloned.map.set("c", { z: 5 });
console.log(original.map.size); // 2(未受影响)
手写递归实现(兼容旧环境或需定制)
当项目需支持 IE、旧版 Node,或要跳过某些字段、转换 Symbol 键、保留函数引用时,需手动实现。关键点有三:
- 用
Object.prototype.toString.call(x)准确判断是否为Map或Set - 对
Map:新建Map,遍历entries(),递归拷贝每个[key, value]对 - 对
Set:新建Set,遍历values()或用扩展运算符[...set],递归拷贝每个成员
同时必须配合 WeakMap 处理循环引用,避免无限递归。
注意 Map 键的特殊性
Map 的键可以是任意类型(包括对象、函数、Symbol),深拷贝时:
- 基本类型键(string/number/Symbol)可直接复用,无需深拷贝
- 对象或函数作键时,通常不建议深拷贝键本身(语义上“键”应保持唯一标识性),实际做法多为保留原引用;若强行拷贝,需额外判断并递归处理,但可能破坏键的相等性逻辑
- 因此主流实现(包括
structuredClone)对对象键仅做浅拷贝或直接复用引用
Set 成员的拷贝逻辑同理
Set 成员如果是基本类型,直接放入新 Set;如果是对象、数组、嵌套 Set/Map,则逐个递归深拷贝。例如:
const s = new Set([{ a: 1 }, new Set([2])]);
// 拷贝后得到新 Set,其中对象 { a: 1 } 是副本,内层 Set 也是副本
不复杂但容易忽略


















