如何让JavaScript中Map/Set的has方法按对象属性值比较?
让Map/Set根据对象属性值判断是否包含元素
这个问题确实很常见——原生的Map和Set默认用引用相等性(SameValueZero算法)来判断元素,所以即使两个对象属性完全一致,只要是不同的引用,has方法就会返回false。要实现你想要的“按属性值判断相等”,我们可以通过自定义封装来解决。
核心思路
本质上,我们需要把对象的“属性值特征”转换成一个可以被原生Map/Set识别的原始类型键(比如字符串、数字),这样不同引用但属性相同的对象,会被映射到同一个键上。
实现方案:自定义ValueMap类
这里我们封装一个继承自Map的类,重写set、has和get方法,自动处理对象到特征键的转换:
class ValueMap extends Map { // 自定义键生成逻辑:根据对象属性生成唯一标识 #generateKey(obj) { // 这里可以根据需求调整:比如只取特定属性,或者更严谨的序列化 // 示例:用JSON.stringify序列化对象,注意属性顺序会影响结果 return JSON.stringify(obj); } set(key, value) { // 如果键是对象(非null),转换为特征键;否则直接用原键 const actualKey = typeof key === 'object' && key !== null ? this.#generateKey(key) : key; return super.set(actualKey, value); } has(key) { const actualKey = typeof key === 'object' && key !== null ? this.#generateKey(key) : key; return super.has(actualKey); } get(key) { const actualKey = typeof key === 'object' && key !== null ? this.#generateKey(key) : key; return super.get(actualKey); } }
测试一下
let map = new ValueMap(); let obj = {name: "Jack"}; map.set(obj, "myObj"); let result = map.has({name: "Jack"}); // 现在返回true console.log(result); // 输出: true
注意事项
- JSON.stringify的局限性:如果对象的属性顺序不同(比如
{name: "Jack", age: 20}和{age: 20, name: "Jack"}),JSON.stringify会生成不同的字符串,导致被判定为不同的键。如果需要忽略属性顺序,可以先把对象的键排序后再序列化。 - 特殊值处理:
JSON.stringify会忽略函数、undefined、Symbol类型的属性,如果你的对象包含这些值,需要自定义更严谨的键生成逻辑(比如递归遍历对象的所有可枚举属性,按固定规则处理特殊值)。 - 针对特定属性的判断:如果你只需要比较对象的某几个属性(比如只看
name),可以修改#generateKey函数,只提取这些属性的值:#generateKey(obj) { return obj.name; // 只根据name属性生成键 }
扩展到Set
同样的思路可以用来封装ValueSet类,只需要把继承的对象换成Set,重写add和has方法即可:
class ValueSet extends Set { #generateKey(obj) { return JSON.stringify(obj); } add(value) { const actualValue = typeof value === 'object' && value !== null ? this.#generateKey(value) : value; return super.add(actualValue); } has(value) { const actualValue = typeof value === 'object' && value !== null ? this.#generateKey(value) : value; return super.has(actualValue); } }
内容的提问来源于stack exchange,提问作者Yos
相关产品推荐
相关产品推荐

