← 中文资源

简体中文版本

JavaScript NaN 比较 — NaN !== NaN

通过使用 Number.isNaN、理解 NaN 不等于自身,并在边界校验数值输入,正确处理 NaN。

阅读英文原文

症状

遇到“JavaScript NaN 比较 — NaN !== NaN”时靠猜测只会浪费时间。请按证据排查:先复现症状,再执行针对性诊断,定位根因后修复,并用明确检查验证结果。

可观察到的症状包括:

  • 某值在与自身比较时永不相等
  • 即使存在 NaN,Array.indexOf(NaN) 仍返回 -1
  • isNaN(“abc”) 为 true,但 “abc” 是字符串而非 NaN

诊断

先在隔离环境中复现故障,再按顺序执行以下检查,逐步缩小范围。

步骤 1

node -e "const n=NaN; console.log(n===n, Object.is(n, n), Number.isNaN(n))"

n === n 为 false;Object.is(NaN, NaN) 与 Number.isNaN(n) 为 true。

步骤 2

node -e "console.log(isNaN('abc'), Number.isNaN('abc'))"

全局 isNaN 会转换并对 “abc” 返回 true;Number.isNaN 返回 false,因为它不是数值 NaN。

常见诱因(按出现频率排序):

  • 用 === 或 !== 比较 NaN,永不匹配
  • 全局 isNaN 会强制转换参数,对非数字字符串产生误报
  • 算术产生 NaN(0/0、非法解析)并静默传播

根因

依据 IEEE 754,NaN 不等于任何值(包括自身),故 === 与 indexOf 永不匹配 NaN。全局 isNaN 会强制转换参数,而 Number.isNaN 专门检测 NaN 值。

修复

用 Number.isNaN 判断 NaN 值、用 Object.is 做把 NaN 视为相等的比较,并校验输入避免 NaN 进入计算;需要真实数字时用 Number.isFinite。

注意:对非数值使用全局 isNaN 会误导;判断值用 Number.isNaN,判断类型用 typeof。

const n = 0 / 0; // NaN

console.log(n === NaN);        // false (always)
console.log(Number.isNaN(n));  // true
console.log(Object.is(n, NaN));// true

// array membership with NaN
const arr = [NaN, 1];
console.log(arr.includes(NaN)); // true (SameValueZero)
console.log(arr.indexOf(NaN));  // -1 (=== comparison)

验证

在测试中断言 Number.isNaN(NaN) 为 true 且 Number.isNaN(“abc”) 为 false;验证 Object.is 将两个 NaN 判为相等。

修复后补充一条回归测试,复现原始症状,防止问题再次出现。

这类故障通常是环境问题而非纯逻辑错误,因此在缺陷记录中应写明完整命令、环境变量与工作目录。请用最小的脚本复现,待最小用例清楚后再扩大范围。为这类故障准备一份简短的排查清单,能显著减少下次排障时间。