set对对象去重只比较引用而非内容,故结构相同的对象仍被视为不同;需通过json.stringify、map键值或自定义深比较实现逻辑去重。

JavaScript 中 Set 对对象去重时,**只比较引用(reference),不比较内容**。所以即使两个对象字面量结构完全一样,只要不是同一个内存地址,Set 就会认为它们不同,导致“本该去重却没去重”的问题。排查关键在于确认你是否在误用对象作为键值,以及是否有意或无意创建了多个独立对象实例。
确认是否真的需要对象去重
多数情况下,用对象做 Set 元素是为了“逻辑唯一性”,比如用户信息、配置项等。但 Set 无法自动识别逻辑相等。你需要先问自己:
- 是否可以用字符串/数字等原始值代替?例如把
{id: 1, name: 'Alice'}转成'1_Alice'或用JSON.stringify(obj)(注意顺序和 undefined/null 的限制) - 是否其实该用
Map+ 自定义 key?比如用user.id作 key,对象本身作 value - 是否在循环中反复新建对象(如
arr.map(x => ({id: x}))),导致每个都是新引用?
快速检测引用是否相同
在怀疑某处对象被重复加入 Set 时,加一行日志验证:
const s = new Set();
const obj1 = { a: 1 };
const obj2 = { a: 1 };
s.add(obj1);
s.add(obj2);
console.log(s.size); // 输出 2
console.log(obj1 === obj2); // false
```
如果输出是 2 且 === 为 false,就坐实了是引用问题,不是 bug,是 JS 机制使然。
替代方案:实现逻辑去重
若必须按内容去重,常见做法有:
-
序列化后存 Set:用
JSON.stringify(obj)得到字符串再塞进Set,适合简单、可序列化的对象(不含函数、undefined、Symbol、Date 等) -
用 Map 手动管理:提取唯一标识字段(如
id或name)作 key,避免重复插入 -
封装一个“值语义 Set”类:内部用数组 +
some()+ 自定义比较函数(如deepEqual),但性能较差,仅用于小数据量
调试技巧:打点看对象来源
在往 Set 添加前,打印对象的构造方式:
set.add(obj);
console.log('added:', obj, 'from', new Error().stack.split('\n')[2].trim());
```
配合断点或 console.trace(),能快速定位是不是每次调用都 new 了一个新对象,比如在 React 渲染函数里直接写 { id: x },就会不断创建新引用。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











