为什么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/0 和 sqrt(-1) 显然不是同一种错误。标准选择让它们彼此不相等,逼你在需要时显式处理异常。
可以把它理解成:NaN 是一个错误占位符,不是某个固定的值。
那怎么判断一个值是不是 NaN
因为 NaN 不等于任何东西(包括自己),不能用 ===:
const x = 0 / 0
x === NaN // false,永远别这么写
Number.isNaN(x) // true,推荐
isNaN(x) // true,但会先 ToNumber,容易误判
isNaN('hello') 为 true,是因为 'hello' 先被转成 NaN;Number.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_/) 转载或引用必须申明原指尖魔法屋来源及源地址!