NaN 是 JavaScript 中主动传播的污染源,源于类型转换失败、非法数学运算或与 undefined 运算,一经产生便在所有算术运算中不可逆扩散,且因 NaN !== NaN 必须用 Number.isNaN() 检测。

NaN 在 JavaScript 复杂表达式中不是“静默错误”,而是主动传播的“污染源”——只要一个操作数是 NaN,整个算术链的结果几乎必然变成 NaN,且不可逆。
NaN 的传播起点:从源头就失效
传播不是偶然,而是设计行为。只要表达式中出现以下任一情况,NaN 就立即生成并进入后续计算:
-
类型转换失败:如
Number("abc")、parseInt("x10")、+"hello" -
非法数学运算:如
0/0、Math.sqrt(-4)、Math.log(-1) -
与 undefined 运算:如
undefined + 5、undefined * 2(注意:null + 1得1,不产生 NaN)
传播路径:逐级传染,无法中断
一旦 NaN 出现,它会像墨水滴入清水一样,在后续所有算术运算中持续扩散:
NaN + 10 → NaNNaN * 3 → NaN-
Math.max(NaN, 42) → NaN(多数 Math 方法对 NaN 返回 NaN) [NaN, 2, 3].reduce((a, b) => a + b) → NaN
即使中间有正常数字、字符串拼接也不会“净化”它——NaN + "px" 得 "NaNpx",但数值语义已彻底丢失。
立即学习“Java免费学习笔记(深入)”;
传播的隐蔽性:相等判断完全失效
这是最易踩坑的一点:NaN 不等于自身,导致常规校验形同虚设:
-
result === NaN永远为 false -
result == NaN同样为 false -
[NaN].indexOf(NaN)返回 -1(找不到) -
Array.includes(NaN)却返回 true(ES2016 特殊处理)
因此,必须用 Number.isNaN(result) 做精准判定——它不尝试类型转换,只认“真 NaN”。
阻断传播:关键防御策略
不能依赖“不出错”,而要主动拦截和兜底:
-
输入清洗:对用户输入或 API 数据,优先用
Number(value)转换后立刻检查Number.isNaN() -
运算前守卫:在关键计算前加保护,例如
const safeAdd = (a, b) => Number.isNaN(a) || Number.isNaN(b) ? NaN : a + b -
默认值兜底:用空值合并或逻辑或时注意——
value || 0对 NaN 无效(因为NaN是 falsy,但你可能不想要 0),应改用Number.isNaN(value) ? 0 : value -
数组聚合防污:用
arr.filter(Number.isFinite).reduce(...)替代直接 reduce,跳过 NaN 和 Infinity


















