一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

如何利用Object.is应对NaN===NaN为false引发的数值去重失效

时间:2026-07-19 17:09:54 编辑:袖梨 来源:一聚教程网

Object.is 能正确识别 NaN 相等,因其采用 SameValue 算法,使 Object.is(NaN, NaN) 为 true,且区分 +0 和 -0;它不进行类型转换,比 === 更严格,适用于精确值比较场景。

Object.is 为什么能正确识别 NaN 相等

NaN === NaN 返回 false,这是 JavaScript 的语言规范决定的——任何值与 NaN 用严格相等比较都为 false。这导致用 SetArray.prototype.filter 去重时,NaN 被当作不同值反复保留。

Object.is 是唯一把 NaN 当作“自身相等”的内置方法:
Object.is(NaN, NaN) 返回 true,且它还修复了 0-0 的误判问题(+0 === -0true,但 Object.is(+0, -0)false)。

注意:Object.is 不是类型转换工具,它比 === 更严格,也更语义准确——它定义的是“SameValue 零值相等算法”,专为精确值比较而生。

用 Object.is 实现数组数值去重(含 NaN)

直接用 Set 无法处理 NaN,因为 Set 内部用的是 SameValueZero 算法(和 === 行为一致),不是 SameValue。必须手动实现去重逻辑:

常见做法是遍历 + Object.is 判重:

function uniqueByObjectIs(arr) {  const seen = [];  return arr.filter(item => {    const notExists = !seen.some(seenItem => Object.is(seenItem, item));    if (notExists) seen.push(item);    return notExists;  });}<p>// 示例uniqueByObjectIs([1, NaN, 2, NaN, 0, -0]); // [1, NaN, 2, 0, -0]

要点:

  • seen.some(...) 必须用 Object.is,不能用 =====
  • 0-0 会被视为不同项,符合预期(若业务需要合并,得额外判断)
  • 性能上是 O(n²),大数据量时慎用;如需优化,可配合 Map 存储已见值的字符串化键(但要注意 NaN 序列化后都是 "NaN",反而会把多个 NaN 合并——所以仍需自定义键生成逻辑)

替代方案:Map + 自定义 key 生成函数

如果数组很大,又必须支持 NaN+0/-0 精确区分,可以借助 Map 实现 O(n) 去重,关键是设计能区分这些值的键:

一个稳妥的 key 生成方式:

function getUniqueKey(val) {  if (Number.isNaN(val)) return 'NaN@' + Math.random().toString(36).slice(2, 8);  if (val === 0 && 1 / val === -Infinity) return '−0';  return String(val);}

但这个方案有缺陷:每次调用 getUniqueKey(NaN) 生成不同 key,破坏一致性。真正可靠的写法是用 Object.is 手动维护键集合,或接受 NaN 在 Map 中只能存一个(因 Map 的 has 方法仍基于 SameValueZero)。所以——

  • 多数场景下,老实用 filter + some + Object.is 更清晰可靠
  • 不要试图用 JSON.stringifyString() 作为去重 key,它们对 NaNundefinedfunction 等都失效
  • Map 本身不解决 NaN 键重复问题,它的 sethas 仍遵循 SameValueZero

在 reduce 或 Set 扩展中误用 Object.is 的典型错误

有人会写:[...new Set(arr.map(x => Object.is(x, x) ? x : x))],这毫无意义——Object.is(x, x) 永远为 true,没改变原始值。

另一个常见错误是混用比较目标:

// ❌ 错误:拿 Object.is 去比较索引和值arr.filter((v, i) => Object.is(v, i))<p>// ✅ 正确:只用于值与值之间比较arr.filter((v, i) => !seen.some(s => Object.is(s, v)))

还有人试图覆盖 Array.prototype.includes 的行为,但该方法内部固定使用 SameValueZero,无法被 Object.is 替代——必须手写逻辑。

最易被忽略的一点:Object.is 是浅比较,对对象或数组仍然无能为力;如果你的“数值”其实是包装对象(如 new Number(NaN)),Object.is 也会返回 false,因为它比的是引用而非包装值。

热门栏目