JavaScript对象数组去重需区分逻辑相等与引用相等:Set因用===比较失效;按字段去重用Map实现O(n);按内容去重可用JSON.stringify(需排序键名)或手动比对,注意函数、undefined等无法序列化。

JavaScript 数组去重时,如果元素是对象,默认的去重方法(如 Set、filter + indexOf)会失效,因为对象比较的是引用地址而非内容。真正去重的关键在于:**判断两个对象是否“逻辑相等”,而不是“引用相同”**。
为什么直接用 Set 无法去重对象数组
Set 内部使用严格相等(===)判断重复,而两个结构完全一样的对象,只要不是同一个引用,就视为不同元素:
const arr = [{id: 1, name: 'a'}, {id: 1, name: 'a'}];
console.log([...new Set(arr)].length); // 输出 2,不是 1
这是因为 {id: 1, name: 'a'} 创建了两次,内存中是两个独立对象,引用不同。
按指定字段(如 id)去重:最常用且高效
适用于对象有唯一标识字段(如 id、name)的场景。用 Map 或 reduce 配合键值映射,时间复杂度 O(n):
立即学习“Java免费学习笔记(深入)”;
- 用
Map缓存已见过的 key,保留首次出现的对象 - 用
filter+findIndex也可行,但性能略低(O(n²))
const arr = [{id: 1, name: 'a'}, {id: 2, name: 'b'}, {id: 1, name: 'c'}];
const uniqueById = [...new Map(arr.map(item => [item.id, item])).values()];
// 结果:[{id: 1, name: 'a'}, {id: 2, name: 'b'}]
按对象内容深度去重:需序列化或自定义比较
当没有明确唯一字段,且需按全部属性值判断是否重复时,可考虑:
- JSON.stringify() 简单序列化:适用于属性顺序固定、不含函数/undefined/Symbol/循环引用的对象
-
手动遍历比对:用
every+hasOwnProperty检查键值对是否全等,支持 NaN、null 等精确比较 -
第三方库辅助:如 Lodash 的
uniqWith+isEqual,处理边界情况更健壮
const arr = [{a: 1, b: 2}, {b: 2, a: 1}, {a: 1, b: 3}];
const seen = new Set();
const uniqueByContent = arr.filter(obj => {
const key = JSON.stringify(Object.keys(obj).sort().map(k => [k, obj[k]]));
return !seen.has(key) && seen.add(key);
});
注意事项与避坑点
实际开发中容易忽略的细节:
- 对象属性顺序影响
JSON.stringify()结果,建议先排序键名再序列化 - 函数、
undefined、Date、RegExp等在 JSON 中会被忽略或转为 null,不可靠 - 避免在大数组中用
includes或indexOf做对象比较,性能差且永远返回 false - 若需保留最后出现的对象而非第一个,把
Map替换为覆盖式赋值即可


















