
本文详解如何对含嵌套结构的 javaScript 对象执行真正意义上的深度克隆,并将其安全存入 map 实例,避免引用共享问题;涵盖 jsON.parse(json.stringify())、结构化克隆(structuredClone)及第三方方案的适用场景与关键限制。
本文详解如何对含嵌套结构的 javascript 对象执行真正意义上的深度克隆,并将其安全存入 map 实例,避免引用共享问题;涵盖 `json.parse(json.stringify())`、结构化克隆(`structuredclone`)及第三方方案的适用场景与关键限制。
在 javascript 中,将一个嵌套对象(如 user)存入 Map 时,若仅做浅拷贝(例如用 Object.assign({}, user)),其嵌套属性(如 address)仍会共享原始引用——修改 map.get(“David”).address.city 将意外影响原对象。因此,真正的深度克隆(deep clone)是保障数据隔离的必要步骤。
✅ 推荐方案:使用 structuredClone()(现代浏览器 & Node.js 17.0+)
structuredClone() 是 ecmascript 标准原生 API,支持大多数常见类型(Object、Array、date、regexp、Map、Set、TypedArray 等),且能正确处理循环引用(而 JSON 方法无法):
let user = { name: "David", age: "23", address: { city: "Toronto", country: "Canada" } }; let map = new Map(); // ✅ 安全、标准、推荐的深度克隆方式 const deepClonedUser = structuredClone(user); map.set("David", deepClonedUser); // 验证:修改克隆体不会影响原始对象 deepClonedUser.address.city = "Vancouver"; console.log(user.address.city); // "Toronto" —— 未被修改 ✅
⚠️ 注意:structuredClone 不支持函数、undefined、symbol、promise、Error 及 dom 节点。若对象含此类值,调用将抛出 DataCloneError。
⚠️ 替代方案:JSON.parse(JSON.stringify())(兼容性高,但有严重限制)
该方法适用于纯 JSON 可序列化的对象(即只含字符串、数字、布尔值、NULL、普通对象和数组):
立即学习“Java免费学习笔记(深入)”;
const deepClonedUser = JSON.parse(JSON.stringify(user)); map.set("David", deepClonedUser);
局限性必须牢记:
- 丢失 Date(变为字符串)、RegExp(变为空对象)、undefined、function、Symbol;
- 丢弃原型链与构造器信息(结果始终是 plain object);
- 无法处理循环引用(直接报错 TypeError: Converting circular structure to JSON);
- BigInt 会抛错(TypeError: Do not know how to serialize a BigInt)。
❌ 不适用方案:Object.assign() 或展开运算符(仅浅克隆)
以下写法不是深度克隆,address 仍为共享引用:
// ❌ 危险!address 对象未被复制 const shallowClone = { ...user }; // 或 Object.assign({}, user) map.set("David", shallowClone); shallowClone.address.city = "Ottawa"; console.log(user.address.city); // "Ottawa" —— 原对象被意外修改!
? 进阶建议:Map 键名设计需规避业务歧义
答案中提到的关键提醒值得强调:map.set(“David”, …) 以姓名为键存在逻辑风险——多名用户同名时将发生覆盖。更健壮的做法是使用唯一 ID(如 _id 或 userId)作为 Map 的 key:
let users = [ { userId: "usr_001", name: "David", address: { city: "Toronto" } }, { userId: "usr_002", name: "David", address: { city: "Montreal" } } ]; const userMap = new Map(); users.forEach(u => { userMap.set(u.userId, structuredClone(u)); // ✅ 深度克隆 + 唯一键 });
✅ 总结:选择策略一览
| 方案 | 兼容性 | 支持循环引用 | 支持函数/Symbol/Date | 推荐场景 |
|---|---|---|---|---|
| structuredClone() | ✅ Node.js ≥17 / chrome ≥98 / firefox ≥94 | ✅ | ❌(除 Date 外多数不支持) | 现代环境首选,安全高效 |
| JSON.parse(JSON.stringify()) | ✅ 所有环境 | ❌ | ❌(Date 变字符串等) | 快速原型、纯数据对象、无特殊类型 |
| Lodash _.cloneDeep() | ✅(需引入) | ✅ | ✅(完整支持) | 项目已用 Lodash,需最大兼容性 |
| 自定义递归克隆 | ⚙️ 可控 | ✅(需手动实现) | ✅(可定制) | 特殊需求(如过滤字段、日志追踪) |
最终实践口诀:优先用 structuredClone();若需支持旧环境或复杂类型,选用成熟库(如 Lodash);永远避免用浅拷贝处理嵌套对象存入 Map 的场景。