我们先看一个题:
const name = "Lydia Hallie";
const age = 21;
console.log(Number.isNaN(name));
console.log(Number.isNaN(age));
console.log(isNaN(name));
console.log(isNaN(age));
答案是:false false true false
1. isNaN()
isNaN函数是用来确定一个值是否是NaN,可以在全局直接使用,返回值是一个布尔值true或false:
// 基础用法
isNaN(1) // false
isNaN(NaN) // true
但是isNaN方法有一些怪异的行为,不然也不会有Number.isNaN函数什么事了:
isNaN(undefined); // true
isNaN({}); // true
isNaN("37,5") // true
isNaN("123ABC") // true
isNaN("abc") // true
可以看到,以上的这些isNaN函数的参数都不是NaN,怎么还返回true呢? MDN的解释是这样的:如果isNaN函数的参数不是Number类型, isNaN函数会首先尝试将这个参数转换为数值,然后才会对转换后的结果是否是NaN进行判断, 也就是说,isNaN的参数首先会执行Number()进行强制转换,然后再去判断是否是NaN:
isNaN(true) // false 因为Number(true)值为1,而1不是NaN, 所以返回false
isNaN(undefined); // true 因为Number(undefined)值为NaN, 所以返回true
isNaN({}); // true 因为Number({})值为NaN, 所以返回true
isNaN('abc'); // true 因为Number(abc)值为NaN, 所以返回true
isNaN(''); // false 因为Number('')值为0, 所以返回false
到这一步很多人可能产生了另一个疑问,为什么Number({})值为NaN,这就涉及到另一个知识点,对象到数字的转换规则
重点来了,有两点
1、如果对象有valueOf()方法并且返回原始值,javascript将返回值转换为数字作为结果
2、如果对象有toString()方法并且返回原始值,javascript将返回结果转换为数字作为结果
3、否则JavaScript抛出一个类型错误异常(throws a TypeError)
请记住一点以上两个方法是内部执行的
举例:
Number([]) // 0
Number([1]) // 1
Number([1,2]) // NaN
因为数组继承了默认的valueOf()方法,这个方法返回一个对象而不是一个原始值,因此,数组到数字的转换则调用toString()方法。
我们依次拆解
Number([]) // 内部一共执行了以下2个步骤
[].toString(). // 返回 ‘’,空字符串,是原始值,这时再调用Number
Number('') // Number 空字符串返回为 0
同样的道理 因为[1].toString 结果是’1’,而[1,2].toString结果是 ‘1,2’
再看我们例子中的Number({})
之所以单独将是因为对象的转换规则比较复杂,
1、先调用对象自身的valueOf方法,如果该方法返回原始类型的值(数值、字符串和布尔值),则直接对该值使用Number方法,不再进行后续步骤。
2、如果valueOf方法返回复合类型的值,再调用对象自身的toString方法,如果toString方法返回原始类型的值,则对该值使用Number方法,不再进行后续步骤。
3、如果toString方法返回的是复合类型的值,则报错。
Number({}) // 内部一共执行了以下3个步骤
{}.valueOf(). // 返回 {},复合类型,这时再调用toString
{}.toString(). // 返回 '[object Object]',是原始值,这时再调用Number
Number( '[object Object]') // 返回 NaN
2. Number.isNaN()
Number.isNaN() 方法确定传递的值是否为 NaN,并且检查其类型是否为 Number,它的返回值是布尔类型。它是原来的全局 isNaN()的更稳妥的版本(注意:ECMAScript 2015版本才有Number.isNaN())。
也就是说,Number.isNaN函数会先检查参数是不是Number类型,如果不是,直接会返回false,只有参数是Number类型才会去判断是不是NaN,我们用Number.isNaN再验证下之前的例子:
// 基本使用
Number.isNaN(NaN) // true
Number.isNaN(1) // false
// 特殊情况
Number.isNaN(true) // false 因为true不是Number类型
Number.isNaN(undefined) // false 因为undefined不是Number类型
Number.isNaN({}) // false 因为{}不是Number类型
Number.isNaN("abc") // false 因为"abc"不是Number类型
Number.isNaN("") // false 因为""不是Number类型
3. 推荐使用Number.isNaN()
通过上述的几个例子可以看到,Number.isNaN用来判断某个值是否是NaN更像是我们理想型用法,不会产生一些比较怪异的行为,使我们的代码更加的严谨,防止出现bug, 所以在写代码的时候更推荐使用Number.isNaN这个方法来确定一个值是否是NaN。