JavaScript Map使用对象作为键时重复条目问题咨询
为什么两次插入“相同”对象后Map的size变成2?
JavaScript里的对象是引用类型,你每次写{someProperty: 0}都会创建一个全新的对象,它们在内存中的引用地址完全不同。而Map判断键是否相等用的是SameValueZero规则——对于引用类型,只有当两个值是同一个内存引用时,才会被视为同一个键。所以你两次调用map.set时用的是两个独立的对象,Map会把它们当成两个不同的键,size自然变成2。
让内容相同的对象作为同一个键的方案
根据不同场景,有几种实用的解决办法:
1. 复用同一个对象引用
如果对象可以提前定义或者重复使用,直接把它存到变量里,每次set都用这个变量:
const map = new Map(); const keyObj = {someProperty: 0}; map.set(keyObj, 0); console.log(map.size); // 1 map.set(keyObj, 0); console.log(map.size); // 1(此时是同一个引用,会覆盖原有值)
2. 将对象序列化为字符串作为键
如果对象的内容是可序列化的(没有函数、undefined、Symbol这类JSON无法处理的属性),可以用JSON.stringify()把对象转成字符串,用这个字符串当Map的键:
const map = new Map(); const getMapKey = (obj) => JSON.stringify(obj); map.set(getMapKey({someProperty: 0}), 0); console.log(map.size); // 1 map.set(getMapKey({someProperty: 0}), 0); console.log(map.size); // 1
注意:这个方法有局限性——如果对象的属性顺序不同(比如{a:1, b:2}和{b:2, a:1}),序列化后的字符串会不一样,会被当成不同的键;另外对象里的JSON不支持类型会丢失信息。
3. 自定义基于内容比较的Map(进阶)
如果需要更灵活的内容比较逻辑,可以自己封装一个类,用深度相等判断来处理键,比如借助lodash的isEqual方法:
import _ from 'lodash'; class ContentMap { constructor() { this.innerMap = new Map(); this.keyList = []; } set(key, value) { const matchIndex = this.keyList.findIndex(k => _.isEqual(k, key)); if (matchIndex !== -1) { // 找到内容相同的键,替换引用并更新值 this.innerMap.delete(this.keyList[matchIndex]); this.keyList[matchIndex] = key; } else { this.keyList.push(key); } this.innerMap.set(key, value); } get(key) { const targetKey = this.keyList.find(k => _.isEqual(k, key)); return targetKey ? this.innerMap.get(targetKey) : undefined; } get size() { return this.innerMap.size; } } // 使用示例 const map = new ContentMap(); map.set({someProperty: 0}, 0); console.log(map.size); // 1 map.set({someProperty: 0}, 0); console.log(map.size); // 1
这个方法能处理复杂对象的内容比较,但性能比原生Map低,适合对键的内容一致性要求高的场景。
内容的提问来源于stack exchange,提问作者serlingpa
相关产品推荐
相关产品推荐

