NaN、isNaN、Number.isNaN你真正理解了吗👻

220 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情

1、NaN特点

对于NaN有以下特点:

  1. typeof是数字(typeof NaN结果为'number'
  2. 不等于自己(NaN == NaN : false、NaN === NaN:false)
  3. 不能被更改(在控制台输入Object.getOwnPropertyDescriptor(window, 'NaN')可以发现configurable是false)

2、isNaN

本质是调用一个toNumber方法,尝试对数据进行转换,如果是NaN就返回true,否则返回false

其语义化代码如下:

const isNaN = function(val) {
  return Object.is(Number(val), NaN);
}

对于不同数据类型在进行toNumber转换时会返回不同结果:

数据类型结果
undefinedNaN
null0
booleantrue转换为1、false转换为0
number不发生转换,返回其本身
string1.如果字符串中只包含数值字符,包括数值字符前的加、减号 则转换为一个十进制数值 (会忽略前面的0) 2.如果字符串包含有效的浮点值,则转换为相应的浮点值(忽略前面的0)3.如果字符串为有效的十六进制格式如:"0xf"则会转为与该十六进制值对应的十进制值 4.如果是空字符串,则转为0 5.如果字符串包含除了以上情况外的其他字符则返回NaN
object调用valueOf()方法并按照字符串转数字规则转换。如果转换结果为NaN,则调用toString()方法,再按照转换字符的规则转换。
symbol抛出异常
bigint抛出异常

由于symbol与bigint在进行toNumber转换时会抛出异常所以isNaN方法并不是安全的

3、Number.isNaN

此方法会先判断这个数据是否为数字(typeof是否为number),如果为数字了再判断该值是不是等于NaN

其语义化代码如下:

Number.isNaN = function(val) {
  if(typeof val !== 'number') {
    return false
  }
  return Object.is(val, NaN);
}

4、严格判断NaN的各种方式

Number.isNaN

可以直接通过ES6新增的Number.isNaN判断,如果是NaN会返回true,不是NaN会返回false

console.log(Number.isNaN(NaN)) // true
console.log(Number.isNaN(2)) // false

自身比较

利用NaN不等于其本身的特点可以进行判断

function isNaNVal(val) {
  return val !== val;
}
console.log(isNaNVal(NaN)); // true

Object.is

Object.is在对比两个值是否相等时接近“===”,但又有以下不同:

  • 使用===判断时 0 与 +0 、-0均相等,而NaN与NaN不相等
  • 使用Object.is判断时0 与 +0相等、0与-0不相等、-0与-0相等;而且NaN等于其本身
function isNaNVal(val) {
  return Object.is(val, NaN);
}
console.log(isNaNVal(NaN));

typeof + isNaN

如果Number上不存在isNaN方法时,可以采用typeof + isNaN的方式来进行判断

function isNaNVal(val) {
  return typeof val === 'number' && isNaN(val)
}

如果Number上有isNaN方法,那么直接采用即可,考虑兼容性的话可以写成如下方式:

if (!("isNaN" in Number)) {
  Number.isNaN = function(val) {
    return typeof val === 'number' && isNaN(val)
  }
}

5、关于NaN的陷阱

先来看如下代码:

const arr = [NaN];
console.log(arr.indexOf(NaN)); // -1
console.log(arr.includes(NaN)); // true

可以看出ES5中的indexOf方法不能够识别NaN,而ES6中的includes方法可以准确识别NaN

这是由于这两种方法底层实现原理不同:

  • indexOf是调用的内部的Number::equal
Number::equal(x, y)
如果x为NaN则返回false、如果y为NaN也返回false,所以是找不到NaN的
  • includes是调用的内部的Number::sameValueZero
Number::sameValueZero(x, y)
如果x为NaN、y为NaN则返回true,所以此方法是能够严格判断出NaN