为什么NaN不等于NaN?

猪不是人,猴子不是人,那么猪会等于猴子吗?

NaN 的全称是 Not a Number,但它自己偏偏是个数值类型里的特殊值。更反直觉的是:

NaN === NaN        // false
NaN == NaN         // false
Object.is(NaN, NaN) // true —— 只有这个认为相等

第一次在项目里踩到这行判断时,我还以为是谁手滑写错了条件。

NaN 从哪来

凡是「数学上说不清结果」的运算,IEEE 754 都规定返回 NaN,例如:

0 / 0
Number('hello')
Math.sqrt(-1)
parseInt('abc', 10)

它们失败原因各不相同,但结果槽位都叫 NaN

为什么不让 NaN === NaN

浮点标准里,NaN 表示无效或未定义的数值结果,不是一个具体的数。

如果规定 NaN === NaN,就等于说「所有算不出来的情况都一样」——但 0/0sqrt(-1) 显然不是同一种错误。标准选择让它们彼此不相等,逼你在需要时显式处理异常。

可以把它理解成:NaN 是一个错误占位符,不是某个固定的值

那怎么判断一个值是不是 NaN

因为 NaN 不等于任何东西(包括自己),不能用 ===

const x = 0 / 0

x === NaN           // false,永远别这么写
Number.isNaN(x)     // true,推荐
isNaN(x)            // true,但会先 ToNumber,容易误判

isNaN('hello')true,是因为 'hello' 先被转成 NaNNumber.isNaN('hello')false,因为它只认真正的 NaN 值。

需要「两个都是 NaN 就算相等」时,用 Object.is

Object.is(NaN, NaN) // true

Object.is 还处理了 +0-0 的区分,那是另一个坑,这里不展开。

工程里怎么用

表单校验、接口解析时,遇到非法数字经常得到 NaN。判断习惯写成:

if (Number.isNaN(value)) {
  // 按无效输入处理
}

数组里找 NaN 也不能用 indexOf

[1, NaN, 3].indexOf(NaN) // -1
[1, NaN, 3].includes(NaN) // true(ES2016 起用 SameValueZero)

所以这不是 JavaScript 故意刁难,而是浮点标准的设计:NaN 表示「这里算坏了」,而不是「大家共用的某个数」。记住 Number.isNaN,比跟 === 较劲省事得多。

版权声明: 本文首发于 指尖魔法屋-为什么NaN不等于NaN?https://blog.thinkmoon.cn/post/903_%E4%B8%BA%E4%BB%80%E4%B9%88nan%E4%B8%8D%E7%AD%89%E4%BA%8Enan_/) 转载或引用必须申明原指尖魔法屋来源及源地址!