症状
遇到“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 判为相等。
修复后补充一条回归测试,复现原始症状,防止问题再次出现。
这类故障通常是环境问题而非纯逻辑错误,因此在缺陷记录中应写明完整命令、环境变量与工作目录。请用最小的脚本复现,待最小用例清楚后再扩大范围。为这类故障准备一份简短的排查清单,能显著减少下次排障时间。