Open toFrankie opened 1 year ago
NaN 是 Not-A-Number 的简写,表示「不是一个数字」的意思。
尽管如此,它却是 Number 类型。
typeof NaN === 'number' // true
平常所写的 NaN 只是「全局变量」的一个「属性」而已,该属性的「初始值」为 NaN。
NaN
为了便于区分,下文中高亮 NaN 表示全局变量的属性,NaN 表示属性值。 类似的还有 undefined、Infinity、globalThis,它们都是全局对象属性。
为了便于区分,下文中高亮 NaN 表示全局变量的属性,NaN 表示属性值。
类似的还有 undefined、Infinity、globalThis,它们都是全局对象属性。
undefined
Infinity
globalThis
前面故意提到 NaN 的初始值为 NaN,原因是在 ES3 时代,该属性是可以被覆盖的,也就是 writable 的(这点与 undefined 表现是一致的),自 ES5 起就不可被重新赋值了。
The value of NaN is NaN.
需要注意的是,NaN 是 JavaScript 中唯一一个不等于其本身的值。这样的话,全等比较结果为 false 是不是看起来合乎情理了?
false
NaN === NaN // false
利用这一特性,可以快速地写出一个判断某个值是否为 NaN 的方法:
const myIsNaN = x => x !== x myIsNaN(NaN) // true
小结:
NaN === NaN
先上几个菜尝尝鲜:
NaN == NaN // false NaN === NaN // false NaN === Number.NaN // false
isNaN(NaN) // true isNaN('NaN') // true isNaN('string') // true isNaN(undefined) // true isNaN({}) // true isNaN('11abc') // true isNaN(new Date().toString()) // true isNaN(null) // false isNaN(10) // false isNaN('10') // false isNaN('10.2') // false isNaN('') // false isNaN(' ') // false isNaN(new Date()) // false
Number.isNaN(NaN) // true Number.isNaN(Number.NaN) // true Number.isNaN(0 / 0) // true Number.isNaN('NaN') // false Number.isNaN('') // false Number.isNaN(' ') // false Number.isNaN(10) // false Number.isNaN(undefined) // false Number.isNaN(null) // false Number.isNaN({}) // false Number.isNaN(new Date()) // false Number.isNaN(new Date().toString()) // false
希望你没有晕,其实掌握内在原理就很简单了,最多是「反直觉」而已...
它是内置对象 Number 提供的一个「静态属性」,其值就是 NaN,且「只读」。
Number
从 ECMAScript 1st Edition #15.7.3.4 可以看到:
The value of Number.NaN is NaN. This property shall have the attributes { DontEnum, DontDelete, ReadOnly }.
这大概就是与全局对象属性 NaN 的唯一区别吧。
它是全局对象的一个方法,用于判断一个值是否为 NaN。
从 ECMAScript 1st Edition #15.1.2.6 可以看到:
Applies ToNumber to its argument, then returns true if the result is NaN, and otherwise returns false.
从规范描述可知,它内部做了「类型转换」。
以 isNaN(x) 为例,它先将 x 转换为 Number 类型(即规范中的 ToNumber 抽象操作),然后再判断转换后的值是否为 NaN,若为 NaN 返回 true,否则返回 false。比如:
isNaN(x)
x
true
const str = 'string' isNaN(str) // true // 相当于 const transformedStr = Number(str) // NaN isNaN(transformedStr) // true
这样的特性有什么用呢,MDN 是这样介绍的,请看这里。
基于此,我们可以快速写出其 Polyfill 方法:
globalThis.myIsNaN = function (value) { const transformedValue = Number(vulue) return transformedValue != transformedValue }
若对数据类型转换不是太熟,可以阅读文章 👉(隐式)数据类型转换详解。
从命名上看,globalThis.isNaN() 它是反直觉的,它偷偷给我们做了一次类型转换。
globalThis.isNaN()
可能正是因为这个原因,所以 ES6 标准中提供了一个全新的方法 Number.isNaN(),其内部逻辑如下(可看):
Number.isNaN()
若入参值不是 Number 类型,则返回 false; 若入参值为 NaN 则返回 true,否则返回 false。
与 isNaN() 不同的是,它不会对传入的值做类型转换。因此,可以快速写出其 Polyfill 方法:
isNaN()
Number.myIsNaN = function (value) { if (typeof value !== 'number') return false return value !== value }
都 2022 年了,都用 Number.isNaN() 来判断吧,其余的就交给 Babel 了。
Number.NaN
globalThis.NaN
Number()
其实在另一篇文章《相等比较详解》已经介绍过了。
const arr = [NaN] arr.indexOf(NaN) // -1 arr.includes(NaN) // true
究其原因,是由于其内部使用了不同的比较算法。
Array.prototype.indexOf()
Array.prototype.includes()
这俩算法对 NaN 的处理如下(详见):
另外,全等比较也使用了 Number::equal (x, y) 算法:
两个 Number 类型的值的比较,其实都是围绕了这三个算法:Number::equal (x, y)、Number:: sameValue (x, y)、Number::sameValueZero (x, y),有兴趣可以看下。它们之中比较特别的值无非就是 +0、-0、NaN。
+0
-0
此前写过不少关于 JavaScript 数据类型的一些文章:
The end.
NaN 的怪诞行为?
NaN 是 Not-A-Number 的简写,表示「不是一个数字」的意思。
尽管如此,它却是 Number 类型。
平常所写的
NaN
只是「全局变量」的一个「属性」而已,该属性的「初始值」为 NaN。前面故意提到
NaN
的初始值为 NaN,原因是在 ES3 时代,该属性是可以被覆盖的,也就是 writable 的(这点与undefined
表现是一致的),自 ES5 起就不可被重新赋值了。需要注意的是,NaN 是 JavaScript 中唯一一个不等于其本身的值。这样的话,全等比较结果为
false
是不是看起来合乎情理了?利用这一特性,可以快速地写出一个判断某个值是否为 NaN 的方法:
小结:
NaN === NaN
的比较结果为false
。isNaN()、Number.isNaN()、Number.NaN 傻傻分不清?
先上几个菜尝尝鲜:
希望你没有晕,其实掌握内在原理就很简单了,最多是「反直觉」而已...
Number.NaN
它是内置对象
Number
提供的一个「静态属性」,其值就是 NaN,且「只读」。从 ECMAScript 1st Edition #15.7.3.4 可以看到:
这大概就是与全局对象属性
NaN
的唯一区别吧。isNaN()
它是全局对象的一个方法,用于判断一个值是否为 NaN。
从 ECMAScript 1st Edition #15.1.2.6 可以看到:
从规范描述可知,它内部做了「类型转换」。
以
isNaN(x)
为例,它先将x
转换为 Number 类型(即规范中的 ToNumber 抽象操作),然后再判断转换后的值是否为 NaN,若为 NaN 返回true
,否则返回false
。比如:这样的特性有什么用呢,MDN 是这样介绍的,请看这里。
基于此,我们可以快速写出其 Polyfill 方法:
Number.isNaN()
从命名上看,
globalThis.isNaN()
它是反直觉的,它偷偷给我们做了一次类型转换。可能正是因为这个原因,所以 ES6 标准中提供了一个全新的方法
Number.isNaN()
,其内部逻辑如下(可看):与
isNaN()
不同的是,它不会对传入的值做类型转换。因此,可以快速写出其 Polyfill 方法:都 2022 年了,都用
Number.isNaN()
来判断吧,其余的就交给 Babel 了。小结
Number.NaN
静态属性的返回值正是 NaN 本身,该属性只读。globalThis.NaN
等价于Number.NaN
,它们的值均为 NaN。isNaN()
比Number.isNaN()
多了一个Number()
过程,实际项目中若要判断一个值是否为 NaN,建议使用后者。indexOf() 和 includes() 对 NaN 是如何判断的?
其实在另一篇文章《相等比较详解》已经介绍过了。
究其原因,是由于其内部使用了不同的比较算法。
Array.prototype.indexOf()
使用了 Number::equal (x, y) 算法。Array.prototype.includes()
使用了 Number::sameValueZero (x, y) 算法。这俩算法对 NaN 的处理如下(详见):
另外,全等比较也使用了 Number::equal (x, y) 算法:
其他
此前写过不少关于 JavaScript 数据类型的一些文章:
The end.