isNan()与Number.isNaN()的区别

例子

大家先看一看下面这个例子,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

isNaN(NaN);

isNaN('A String');

isNaN(undefined);

isNaN({});

Number.isNaN(NaN);

Number.isNaN('A String');

Number.isNaN(undefined);

Number.isNaN({});

如果你能很清楚答案,那么这篇文章你可以略过。
不清楚的朋友,我们来慢慢来分析。

答案如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
isNaN(NaN); // true

isNaN('A String'); // true

isNaN(undefined); // true

isNaN({}); // true

Number.isNaN(NaN); // true

Number.isNaN('A String'); // false

Number.isNaN(undefined); // false

Number.isNaN({}); // false

为什么看起来同样的函数,得出的结果为什么不同呢?

NaN 是什么?

在解释 NaN 之前,我想先解释下 type/value/variable 这个几个容易混淆的概念,已经很清楚的朋友可以跳过这一小节

type / value / variable 是什么

在 JavaScript 中,value一共有七种type

  1. null
  2. undefined
  3. boolean
  4. number
  5. string
  6. object
  7. symbol (ES6新增)

那么,variable是什么呢?就是我们平时 var 之后的声明的那个东西。

type, value, variable 之间的关系可以这么说:variable是存放value的容器,而value是有着type概念的,但是容器variable是没有type的概念的,举个例子

1
2

var a = 'foo';

容器 variable a 装着 value 'foo', value 'foo' 的type是string

NaN

MDN里面这么描述

The global NaN property is a value representing Not-A-Number.

意思是是说:NaN是一个放在 global(浏览器里是window)对象里的一个value,是一个代表Not-A-Number的value.

意思还是很含糊。

那么我们在看神书《You Don’t Know JS》里的描述

NaN literally stands for “not a number”, though this label/description is very poor and misleading, It would be much more accurate to think of NaN as being “invalid number,” “failed number,” or even “bad number,” than to think of it as “not a number.”

根据上一个小结的知识,我们知道了,NaN是一个 value, 这个 value 的 type 是 number。

但是跟普通的type是number的value不一样的是,NaN 代表 ‘Not a number’ 这一意义。

那么问题来了,怎么判断一个 value 是不是 NaN 呢?

isNaN()

也许有人会说,判断还不容易吗?直接比较不就好了。

1
NaN === NaN // false

NaN 跟它自己比较会返回false。

所以,我们就需要一个特殊的函数来判断一个value是不是NaN了。

isNaN() 就横空出世了。

我们再回头看一看上面的例子

1
isNaN(NaN); // true

OK, 成功了,看似很完美,但是接着看以下例子

1
2
3
4
5
isNaN('A String'); // true

isNaN(undefined); // true

isNaN({}); // true

会发现,很明显不是 NaN 的 value 也被误判成 NaN 了。

这个BUG已经存在了20年,从JavaScript最开始就一直存在。很明显当初的设计者,在设计isNaN()的时候,局限了在 “Not a Number” 这一字面意思上了:只要不是number就会返回 true。

于是 ES6 为了弥补这一BUG(而不是修正,因为isNaN存在时间太长,有可能很多功能都是基于这个BUG之上的)引入了 Number.isNaN().

1
2
3
4
5
6
7
Number.isNaN(NaN); // true

Number.isNaN('A String'); // false

Number.isNaN(undefined); // false

Number.isNaN({}); // false

回头看上面的例子,就明白了修复了什么问题。

Number.isNaN() 的 polyfill

没有ES6的情况下,可以采用以下polyfill

1
2
3
4
5
6
7
8
if (!Number.isNaN) {
Number.isNaN = function(n) {
return (
typeof n === "number" &&
window.isNaN( n )
);
};
}

简单来看,就是在原有 isNaN() 的基础上增加了一个 type 的判断,因为 NaN 的 type 是 number。

还有一种更加简单的实现

1
2
3
4
5
if (!Number.isNaN) {
Number.isNaN = function(n) {
return n !== n;
};
}

利用了只有 NaN 不跟自己相等的特性。

顺便吐槽一下MDN的解释,他是这么解释 isNaN()

You could think of isNaN as:

1
2
3
var isNaN = function(value) {
return Number.isNaN(Number(value));
}

他是在ES6新函数Number.isNaN()的基础上,去解释旧函数isNaN()的。

不过我们可以通过以上方式来解释判断 isNaN() 为什么会出现

1
2
3
4
5
isNaN('A String'); // true

isNaN(undefined); // true

isNaN({}); // true

这样的情况了。