JavaScript数组去重需用Object.is处理NaN,因NaN!==NaN;Map+Object.is可稳定去重并区分+0/-0;Set或indexOf等常规方法对NaN失效。

JavaScript 中数组去重时,NaN 是个特殊值:它不等于自身(NaN !== NaN),所以常规的 includes、indexOf 或 Set 构造都会失败——Set 会把多个 NaN 当作不同元素保留,而基于相等性判断的方法则完全跳过 NaN 的匹配。
用 Map + Object.is 实现稳定去重
Object.is() 是 ES6 提供的严格相等判断方法,它能正确识别 NaN === NaN(即 Object.is(NaN, NaN) === true),同时还能区分 +0 和 -0。结合 Map 可以安全记录已出现的值:
- 遍历原数组,对每个元素用
Object.is检查是否已在 Map 的键中存在 - 如果不存在,存入 Map 并推入结果数组;存在则跳过
- 这样既能处理 NaN、+0/-0,也兼容对象、函数等引用类型(按引用去重)
function uniqueByObjectIs(arr) {
const seen = new Map();
const result = [];
for (const item of arr) {
let found = false;
for (const key of seen.keys()) {
if (Object.is(key, item)) {
found = true;
break;
}
}
if (!found) {
seen.set(item, true);
result.push(item);
}
}
return result;
}
// [1, NaN, 'a', NaN, -0, +0] → [1, NaN, 'a', -0]
用 Set + 自定义包装器(推荐用于简单值)
原生 Set 不支持自定义比较逻辑,但可以将数组元素转换为“可比较的唯一字符串标识”。对基本类型(含 NaN),可用 JSON.stringify 配合类型前缀来规避 NaN 和 +0/-0 问题:
- 对
NaN显式转为字符串"NaN" - 对
+0和-0分别标记为"+0"和"-0" - 其他值用
String()或JSON.stringify()(注意对象需保证结构一致)
function uniqueByStringKey(arr) {
const seen = new Set();
return arr.filter(item => {
const key =
Number.isNaN(item) ? 'NaN' :
item === +0 ? '+0' :
item === -0 ? '-0' :
String(item);
if (seen.has(key)) return false;
seen.add(key);
return true;
});
}
处理嵌套对象或复杂结构的去重
若数组包含对象、数组等嵌套结构,且希望按“深度相等”去重(例如 {a:1} 和 {a:1} 视为重复),就不能依赖 Object.is 或字符串键了。此时需要深比较:
- 可借助
lodash.isEqual等库做逐字段比对 - 或手写简易深比较函数(注意循环引用、Date、RegExp 等特殊类型)
- 再配合双层循环或 Map 缓存已比对过的对象引用 + 序列化结果
对含 NaN 的对象,如 {x: NaN} 和 {x: NaN},深比较必须用 Object.is 判断属性值,否则仍会误判为不等。
避开陷阱:哪些方法不适用?
以下常见方式在含 NaN 时会出错:
-
[...new Set(arr)]:NaN 被当作不同值,保留多个 -
arr.filter((v, i) => arr.indexOf(v) === i):indexOf(NaN)始终返回 -1,导致所有 NaN 全部保留 -
arr.reduce((acc, cur) => acc.includes(cur) ? acc : [...acc, cur], []):同理,includes对 NaN 返回 false
含 NaN 的数组去重,核心是换掉“抽象相等(==)”或“严格相等(===)”,改用 Object.is 或语义明确的键生成策略。根据数据复杂度选择 Map + Object.is(通用)、字符串键(简单值)、或深比较(嵌套结构)。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











