全局属性 NaN 是一个表示非数字的值,为 Number 类型,意为 Not a Number (不是一个数字)。
NaN 是全局对象(global object)的一个属性,即它是一个全局变量。在现代浏览器中,NaN 是一个不可配置、不可写的属性。即使不是这样,也要避免重写它。在程序中很少使用 NaN。
NaN 的初始值为 Number.NaN。
NaN 是 JavaScript 中的假值之一。
产生 NaN 的操作
有五种不同类型的操作返回 NaN:
- 失败的数字转换(例如,显式转换,如
parseInt("blabla")、Number(undefined),或隐式转换,如Math.abs(undefined)) - 计算结果不是实数的数学运算(例如,
Math.sqrt(-1)) - 不定式(例如,
0 * Infinity、1 ** Infinity、Infinity / Infinity、Infinity - Infinity) - 一个操作数被强制转换为
NaN的方法或表达式(例如,7 ** NaN、7 * "blabla")——这意味着NaN具有传染性 - 将无效值表示为数字的其他情况(例如,无效的
Date new Date("blabla").getTime()、"".charCodeAt(1))
NaN 的行为
NaN 及其行为不是 JavaScript 发明的。它在浮点运算中的语义(包括 NaN !== NaN)是由 IEEE 754 指定的。NaN 的行为包括:
- 如果
NaN涉及数学运算(但不涉及位运算),结果通常也是NaN。(指数为 0 求幂除外) - 当
NaN是任何关系比较(>,<,>=,<=)的操作数之一时,结果总是false。 NaN不等于(通过==、!=、===和!==)任何其他值——包括与另一个NaN值。
NaN 通过数学运算进行传播,因此通常在计算结束时测试 NaN 一次就足以检测错误条件。NaN 被静默转义的唯一情况是使用指数为 0 求幂时,它立即返回 1 而不测试基数的值。
NaN ** 0 === 1; // true
NaN 字符串相加会转换成字符串 'NaN'。(字符串连接运算不是数学运算)
NaN + 1 // NaN
NaN + "NaN" // 'NaNNaN'
此外,一些数组方法不能找到 NaN,而另一些可以。也就是说,查找索引的(indexOf()、lastIndexOf())不能找到 NaN,而查找值的(includes())可以:
const arr = [2, 4, NaN, 12];
arr.indexOf(NaN); // -1
arr.includes(NaN); // true
// Methods accepting a properly defined predicate can always find NaN
arr.findIndex((n) => Number.isNaN(n)); // 2
判断 NaN
要判断一个值是否为 NaN,可以使用 Number.isNaN() 或 isNaN() 来最清楚地确定一个值是否为 NaN——或者,因为 NaN 是唯一与自身不相等的值,所以你可以执行类似 x !== x 这样的自我比较。
-
isNaN()函数用来确定一个值是否为NaN,若有必要,则首先将值转换为数字。由于isNaN()函数内部的强制类型转换可能令人迷惑,因此你可能更想要使用Number.isNaN()。 -
Number.isNaN()静态方法判断传入的值是否为NaN,如果输入不是数字类型,则返回false。它是全局isNaN()函数更健壮的版本。
NaN === NaN; // false
Number.NaN === NaN; // false
isNaN(NaN); // true
isNaN(Number.NaN); // true
Number.isNaN(NaN); // true
function valueIsNaN(v) {
return v !== v;
}
valueIsNaN(1); // false
valueIsNaN(NaN); // true
valueIsNaN(Number.NaN); // true
但是,请注意 isNaN() 和 Number.isNaN() 之间的区别:如果当前值是 NaN,或者将其强制转换为数字后将是 NaN,则前者将返回 true。而后者仅当值当前为 NaN 时才为 true:
isNaN("hello world"); // true
Number.isNaN("hello world"); // false
出于同样的原因,使用 BigInt 值时 isNaN() 会抛出错误,而 Number.isNaN() 不会,直接由于类型不符而返回 false:
isNaN(1n); // TypeError: Conversion from 'BigInt' to 'number' is not allowed.
Number.isNaN(1n); // false
为什么 NaN 不等于 NaN
NaN 与自身不平等的动机,是可以用不同的二进制表示生成两个都是 NaN 的浮点数,因为在 IEEE 754 编码中,任何指数为 0x7ff 且尾数非零的浮点数都是 NaN。
示例:不同的二进制表示的均为 NaN 的数
在 JavaScript 中,你可以使用类型化数组来进行位操作。
const f2b = (x) => new Uint8Array(new Float64Array([x]).buffer);
const b2f = (x) => new Float64Array(x.buffer)[0];
// Get a byte representation of NaN
const n = f2b(NaN);
// Change the first bit, which is the sign bit and doesn't matter for NaN
n[0] = 1;
const nan2 = b2f(n);
console.log(nan2); // NaN
console.log(Object.is(nan2, NaN)); // true
console.log(f2b(NaN)); // Uint8Array(8) [0, 0, 0, 0, 0, 0, 248, 127]
console.log(f2b(nan2)); // Uint8Array(8) [1, 0, 0, 0, 0, 0, 248, 127]
参考
NaN
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/NaN
Number.NaN
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number/NaN
isNaN()
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/isNaN
Number.isNaN()
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN