set是javascript数组去重最简且可靠的方式,能正确处理nan、undefined、+0与-0等边界值;filter+indexof/includes因nan!==nan易出错;手动实现需用number.isnan()或object.is进行特殊判断。

JavaScript 数组去重时,NaN 是个特殊值:它不等于自身(NaN === NaN 为 false),所以常规的基于相等性判断的方法(如 filter() + indexOf()、Set 构造、或 includes())在默认行为下可能无法正确识别重复的 NaN —— 实际上,现代 Set 已正确处理 NaN 去重,但其他方法容易出错,需特别注意。
Set 是最简且可靠的方式(支持 NaN)
Set 在 ES6+ 中对 NaN 的处理是符合规范的:它把所有 NaN 视为同一个值。因此以下写法能正确去重,包括多个 NaN 只保留一个:
const arr = [1, NaN, 2, NaN, 'a', undefined, NaN]; const unique = [...new Set(arr)]; // → [1, NaN, 2, 'a', undefined]
这是目前推荐的默认方案:简洁、高效、语义清晰,且天然兼容 NaN、undefined、-0、+0 等边界值(Set 认为 +0 === -0,也只留一个)。
filter + indexOf / includes 会漏掉 NaN
这些方法依赖 === 判断,而 NaN !== NaN,导致 indexOf(NaN) 总返回 -1,includes(NaN) 虽然规范中专门修复了对 NaN 的支持([NaN].includes(NaN) === true),但 filter 配合 indexOf 仍不可靠:
-
arr.indexOf(NaN)永远返回-1→ 所有 NaN 都被当作“首次出现”保留,无法去重 -
arr.includes(NaN)虽返回true,但用于去重逻辑时需小心索引位置,易写出错误逻辑
例如:
const arr = [NaN, NaN, 1]; arr.filter((v, i) => arr.indexOf(v) === i); // → [NaN, NaN, 1] ❌
手动实现兼容 NaN 的去重(需显式判断)
如果必须用 reduce 或自定义逻辑,需用 Number.isNaN() 单独处理 NaN:
- 维护一个已见值数组(
seen) - 对每个元素
v,检查是否已存在:seen.some(item => item === v || (Number.isNaN(v) && Number.isNaN(item))) - 或更简洁地用
Array.prototype.findIndex配合自定义比较
示例:
const arr = [NaN, 1, NaN, 'a', NaN];
const unique = arr.reduce((acc, v) => {
const hasNaN = Number.isNaN(v);
const exists = acc.some(item =>
hasNaN ? Number.isNaN(item) : item === v
);
return exists ? acc : [...acc, v];
}, []);
// → [NaN, 1, 'a'] ✅
注意 Object.is 的行为(更严格相等)
Object.is(NaN, NaN) === true,且它还能区分 +0 和 -0(Object.is(+0, -0) === false)。若业务要求严格区分 +0 和 -0,可用 Object.is 替代 ===:
const arr = [+0, -0, NaN, NaN]; const unique = arr.filter((v, i) => arr.findIndex(item => Object.is(item, v)) === i ); // → [+0, -0, NaN] ✅(+0 和 -0 都保留)
但多数场景无需如此精细,Set 的行为已足够合理。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











