Set无法对对象数组内容去重,因其基于内存引用(===)判断;按字段(如id)去重推荐Map映射,按内容去重可用JSON.stringify(限纯数据且键序一致)或lodash.uniqWith深比较。

JavaScript 的 Set 本身无法直接对复杂对象(如普通对象、数组)做内容去重,因为它只用 === 判断相等——两个结构一模一样的对象,只要内存地址不同,就被视为不同元素。所以处理对象去重,关键不是“改 Set”,而是“绕过它的引用限制”,用合适策略提取唯一性。
按某个字段(如 id)去重最常用
多数业务场景中,对象有唯一标识(比如 id、uuid 或 name),这时推荐用 Map 配合键值映射,既清晰又高效:
- 用对象的唯一字段作为 Map 的 key,对象本身作 value,重复 key 会自动覆盖
- 最后调用
Map.values()转成数组即可
示例:
const arr = [{id: 1, name: 'a'}, {id: 2, name: 'b'}, {id: 1, name: 'c'}];<br>const unique = Array.from(new Map(arr.map(item => [item.id, item])).values());<br>// → [{id: 1, name: 'a'}, {id: 2, name: 'b'}]
按整个对象内容去重(需谨慎)
当没有固定唯一字段,又确实要按“内容完全相同”判定时,可序列化为 JSON 字符串再用 Set 去重,但要注意限制:
- 仅适用于纯数据对象:不能含函数、
undefined、Date、RegExp、Symbol或循环引用 - 属性顺序必须一致,否则
JSON.stringify({a:1,b:2}) !== JSON.stringify({b:2,a:1}) - 建议先标准化键序(如排序后 stringify),或改用更健壮的深比较方案
基础写法:
const seen = new Set();<br>const unique = arr.filter(obj => {<br> const str = JSON.stringify(obj);<br> if (seen.has(str)) return false;<br> seen.add(str);<br> return true;<br>});
成员检测与管理仍可用 Set,但需配合包装
如果只是想快速查某个对象是否“逻辑存在”(比如已选中、已缓存),不一定要把原始对象塞进 Set。可以:
- 用唯一字段构造字符串 key,存入普通 Set:
new Set(['id-1', 'id-2']) - 或封装一个带 key 映射的类,内部用 Map 管理对象,对外暴露
has(id)、get(id)、add(obj)等方法 - 避免直接对对象调用
set.has(obj),那永远返回false(除非是同一个引用)
需要真正深比较时,别硬刚
手写可靠的深比较成本高、边界多。生产环境建议用成熟工具:
- Lodash:
_.uniqWith(arr, _.isEqual) - 如果是 TypeScript 项目,也可考虑
fast-deep-equal等轻量库做自定义去重逻辑 - 不要为了“看起来用 Set”而强行在循环里反复
JSON.stringify,性能和可维护性都受损


















