JavaScript Map获取对象后修改为何同步原映射值?如何避免?
这个问题其实是JavaScript引用类型的核心特性导致的,我来给你拆解清楚:
为什么修改obj会同步改变Map里的值?
在JavaScript中,对象(包括数组、Map、Set这类复杂类型)都属于引用类型。当你执行map1.set('bar', {"foo":"baz"})时,Map并没有存储这个对象的完整副本,而是存储了一个指向该对象在内存中位置的引用地址。
当你用const obj = map1.get('bar')获取对象时,obj拿到的是和Map里完全相同的引用——换句话说,obj和Map中存储的内容指向的是同一个内存对象。所以你修改obj.foo = "abc"时,本质是在修改内存里的原始对象,Map里的引用指向的还是这个对象,自然会同步看到变化。
如何阻止这种行为?
除了你提到的Object.assign(),还有几个更灵活的方案,分场景来看:
1. 浅拷贝(适合无嵌套的简单对象)
浅拷贝会复制对象的第一层属性,操作起来简洁高效,比Object.assign()更直观的写法是用扩展运算符:
const obj = {...map1.get('bar')}; obj.foo = "abc"; console.log(map1.get("bar")); // 输出 {"foo":"baz"},原Map内容不受影响
Object.assign({}, map1.get('bar'))和{...map1.get('bar')}效果完全一致,都是浅拷贝。但要注意:如果对象里有嵌套的子对象,修改子对象的属性还是会影响原Map中的内容。
2. 深拷贝(处理多层嵌套对象)
如果你的对象存在多层嵌套,浅拷贝就不够用了,这时候需要深拷贝。原生环境下最常用的方案是利用JSON序列化:
const obj = JSON.parse(JSON.stringify(map1.get('bar'))); obj.foo = "abc"; console.log(map1.get("bar")); // 输出 {"foo":"baz"}
⚠️ 注意:这个方法有局限性——无法处理函数、Symbol类型、循环引用的对象,如果你的对象包含这些内容,会导致序列化失败或丢失数据。
如果需要更可靠的深拷贝,可以自己写一个递归深拷贝函数:
function deepClone(obj) { if (obj === null || typeof obj !== 'object') return obj; // 处理日期对象 if (obj instanceof Date) return new Date(obj.getTime()); // 处理数组 if (obj instanceof Array) return obj.map(item => deepClone(item)); // 处理普通对象 const clonedObj = {}; for (const key in obj) { if (obj.hasOwnProperty(key)) { clonedObj[key] = deepClone(obj[key]); } } return clonedObj; } const obj = deepClone(map1.get('bar')); obj.foo = "abc"; console.log(map1.get("bar")); // 输出 {"foo":"baz"}
3. 存入Map时提前存副本(一劳永逸)
如果不想每次获取对象都要做拷贝操作,可以在往Map中存入对象时就保存一个副本,这样后续无论怎么修改get出来的对象,都不会影响Map里的原始内容:
const originalObj = {"foo":"baz"}; const map1 = new Map(); // 存入时浅拷贝,适合简单对象 map1.set('bar', {...originalObj}); const obj = map1.get('bar'); obj.foo = "abc"; console.log(map1.get("bar")); // 输出 {"foo":"baz"}
如果是嵌套对象,就用深拷贝存入:
map1.set('bar', deepClone(originalObj));
内容的提问来源于stack exchange,提问作者Vaibhav Patil

