先记住这个答案
NaN 是唯一不等于自身的值,因为 IEEE 754 规定 NaN 与任何值(包括自身)比较都不相等。判断时推荐 Number.isNaN(value),它只检查值是否为 NaN,不做类型转换。x !== x 也可行,但不直观。全局 isNaN() 会先将其参数强制转换为数字,因此对字符串如 'hello' 也会返回 true,应避免使用。
- NaN 不等于自身是 IEEE 754 规定
- Number.isNaN 不强制转换更可靠
- 全局 isNaN 会先转数字用于非数值
NaN 语义:IEEE 754 与 JavaScript 实现
NaN 是 IEEE 754 浮点标准中定义的特殊值,表示“非数字”。语法规定,NaN 与任何值(包括自身)进行相等比较(==、===)时结果均为 false。这是因为 NaN 通常表示未定义或不可表示的运算结果,如 0/0 或 Infinity-Infinity,没有一个确定的数值可比。
JavaScript 遵循该标准,因此 NaN === NaN 返回 false。同时,涉及 NaN 的关系运算如 >、< 也总为 false。NaN 参与数学运算会传染,但特殊情况:NaN ** 0 结果为 1,因为按规范幂运算指数为 0 时先返回 1,不检测基数。此外,数组 indexOf 无法找到 NaN,而 includes 可以,因为 includes 使用 SameValueZero 比较。
表单输入校验:区分非法数字和空字符串
假设一个前端表单需要将用户输入转换为年龄,输入框可能为空、普通数字或任意字符串。期望:空字符串保留为空,非数字字符串提示错误,数字则通过。若使用全局 isNaN,当输入为空字符串时,isNaN('') 先转换 Number('') 为 0,返回 false,导致误判为合法年龄,用户点击提交后看到年龄 0。
改用 Number.isNaN(Number(input)) 时,需先排除空字符串,因为 Number('') 为 0,若不先判断,会将空字符串误判为合法年龄。可先检查 input.trim() === '',若空则保留为空,否则再转换并用 Number.isNaN 检测;非法输入如 'abc' 转换后为 NaN,可正确提示错误。这样可避免全局 isNaN 的类型转换陷阱,确保校验逻辑可控。
适用边界:不同判断方法的语义与局限
Number.isNaN 只对真正的 NaN 值返回 true,对其他任何类型包括字符串、对象都返回 false。它严格区分“非数值”和“数值”。但当你需要判断一个值能否被成功转换为数字,如用户输入的字符串是否可解析,Number.isNaN 本身不适用,因为它不做转换,需先 Number() 转换再检测,但 Number('') 是 0,Number(' ') 也是 0,这可能是业务不希望接受的。
全局 isNaN 试图转换参数,对空字符串返回 false,但也会将 null 等值转为 0,容易产生误判。此外,对 BigInt 参数,isNaN(1n) 抛出 TypeError,而 Number.isNaN(1n) 返回 false。在性能上,Number.isNaN 更高效且语义清晰。建议在代码中始终使用 Number.isNaN,除非你明确需要检测“能否被转换数字”的语义,那种场景应使用 Number 转换并进行额外的边界检查。
容易答错的地方
- 误用全局 isNaN
- 很多开发者直接 isNaN(value) 判断,但会先进行数字转换。例如 isNaN('hello') 返回 true,因为它被转换为 Number('hello') 得 NaN。正确做法是 Number.isNaN,它不会转换,只有真正的 NaN 才返回 true。
- 认为 NaN 是全局属性,可覆盖
- 在旧环境 NaN 是可写属性,可能被用户代码覆盖,导致 NaN === NaN 可能为 true(如果被赋值为普通数字)。现代浏览器中 NaN 只读,但依赖该行为不够安全,应使用 Number.NaN 或 Number.isNaN 而非全局变量的值。
面试官还会怎么问?
为什么 xxx === xxx 对 NaN 返回 false?
严格相等算法比较时,如果操作数是 NaN,则直接返回 false,不论另一侧是否相同。这是 ECMAScript 规范中明确规定的,也是 IEEE 754 的语义。
Object.is(NaN, NaN) 和 === 有区别吗?
Object.is 遵循 SameValue 算法,认为 NaN 等于自身,且能区分 +0 和 -0;而 === 不认为 NaN 相等,且认为 +0 与 -0 相等。
如何判断一个字符串是否能转换为数字?
使用 Number() 转换,然后用 Number.isNaN 结果,但注意空字符串和 null 转 0,可能需额外检查:if (value === '') return false; if (value === null) return false; 或使用 parseFloat 与字符串全等比较,但会忽略尾部字符。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。