JS0015 重温NaN
1.引子
Amos学弟下午在群里吐槽了下NaN,顺手发挥,水文题材有了
之前写过一篇JS 数据类型的爽文, 感兴趣的可以自己翻翻。
今天主要和大家重温一下NaN 这个奇怪的家伙。
2.定义
IEEE标准定义了一个数字常数NaN, 且规定其与自身不相等。
NaN 代表NotaNumber , 但它的类型仍然是Number, 它其实是占用了2^53 (JS中Number类型的最大值是2^53 - 1), 这意味着你可以使用typeof对其进行检测。
你可能会问他为什么仍是数字类型,别急,往下读,带你进入一个很有趣的思考环节
typeof NaN // 'number'
NaN === NaN // false
NaN !== NaN // true
parseInt('阿吉') // NaN
parseInt('阿吉') === NaN // false
isNaN() // true
isNaN(NaN) // true
或许在初学JS时,你会对 NaN 不等于自身的特性感到奇怪,甚至可能会怀疑这是一个BUG,毕竟NaN是 JS 中唯一一个不等于自身的值。
但你仔细思考下 NaN 真正的含义之后,它其实是个很正确的设计,它是JS的一个特性。
NaN的本质不是告诉你它是什么,而是告诉你它不是什么。
它的本质代表一个未定义的数字,它是未定义状态的一种占位符。
现在我用一种通俗的方式给你表达,我给你两个盒子A、B,告诉你A盒子没有苹果,B盒子也没有苹果。你会觉得A盒子和B盒子是一样的吗?不会的,因为可能A盒子装的是葡萄,B盒子装的是草莓,他们都不是苹果。这完全符合我告诉你的事实。
这是不是很有趣?那么我们再接着思考下,如果 NaN 等于自身,将这个特性作为正解,那会带来什么代价。
NotaNumber 最容易让人想到的是错误的数学计算(当然啦基于JS特性,也会有类型转换)。
现在想象一下这种情况,你完成了两个复杂的数学计算,然后要比较它们的结果。你希望仅当两个结果是相同的数字时,才期望它们相等。如果两个计算都存在输入/计算过程错误,导致二者都返回了NaN,此时我们的期望是不相等,因为它们并不是同一个我们期待的正确的数字。而此时基于你的设计特性:NaN恒等自身,返回的是相等。这就是一个不合理的设计。
再让我们回到定义时的想法为什么NaN是数字类型。结合它的定义,数字类型才是它真正的归属。因为只有它是数字类型,它调用Number上的任何方法才不会报错。不然回到错误的数学计算这种情况,源于数学计算的NaN由于不是Number类型而调用Number方法,带来的报错导致程序中断,这种情况更糟糕。所以,如果NaN不是数字类型,那必须在进行任何数学运算之前对其进行检查,这也是为什么错误的数字计算结果我们使用NaN来表示,而不是使用undefined和null。
3.如何得到NaN
得到NaN的方式上面已经提及过了, JS保留词NaN、错误的数学计算、类型强制转换。
对JS类型相关感兴趣,自行阅读类型转换)、JS装箱拆箱、JS类型判断
/** JS 保留词 NaN */
NaN // NaN
/** 错误的数学计算 */
12 - '阿吉' // NaN
0 / 0 // NaN
/** 类型强制转换 */
parseInt('a') // NaN
Number('阿吉') // NaN
Number([1,2]) // NaN
+'阿吉' // NaN
+function(){alert('123')}() // NaN
-function(){alert('123')}() // Na