前言
随着笔者写 JavaScript 的时间越来越长,遇到的坑也越来越多😂,在踩坑的过程中也发现了一些有趣的冷知识,因此笔者就想写成一个系列文来记录这些有趣的冷知识,废话不多说马上开始今天的文章吧!
NaN
首先要介绍的是在 JavaScript 中让人又爱又恨的 NaN , NaN 的全名为: Not a Number ,翻成中文就是这个值并不是一个数字,通常都会发生在进行数字的转换上会返回的结果,例如以下这段代码:
const a = 'abc123'
console.log(parseInt(a)) // NaN
大家都知道 JavaScript 是一个弱类型的语言,所以当今天需要确认此变量为什么类型时,必须要用 typeof 这个方法来判断,接下来笔者就有个小问题要考考大家了,请大家猜猜看底下的代码会返回什么类型。
typeof NaN // ?
答案揭晓!那就是 number
所以 NaN 用白话文翻译来说就是:本身是数字类型但却不是数字的数字。
其实也不难想像为什么 JavaScript 会这样定义,回到笔者上面说的 NaN 通常都是发生在要转换成数字时发现不能用任何一个数字来表现,由于本身经过转换已经是数字的类型了,也因此 NaN 的类型才会是 number 。
接下来再考考大家下面这段判断式会返回 true 还是 false。
NaN === NaN
答案是返回 false。
其实早在 IEEE 754 的规范中就有定义如果判断式遇到 NaN 就要返回 false ,但应该大多数的读者跟笔者一样都不会刻意去看这种文件吧 XD,这时候就可以多看看 ECMAScript 的规范,里面也会提到一些跟 NaN 有关的比较说明,这样也可以避免踩到非常多的地雷。
判断 NaN
这时候你可能会问那我要怎么判断是不是 NaN 呢?方法也很简单,JavaScript 自己有提供一个方法叫 isNaN( ) 通过这个方法很快就可以知道是不是 NaN 了。
const a = 'abc123'
const b = parseInt(a)
console.log(b) // NaN
console.log(isNaN(b)) // true
但其实 isNaN 也有小地雷,由于 isNaN 一开始会做 ToNumber() 的数值转换,这时候假如遇到的是空字符串或 Boolean 很容易就会因为先做数值转换的关系让最终结果变的不是我们预想的结果,这时候就可以利用 Number.isNaN() 的方式来进行判断, Number.isNaN() 并不会进行 ToNumber() 的数值转换,因此最终结果也比较符合大家所设想的结果。
但除了上述的技巧外,也有一个非常取巧的方式可以检查 NaN ,还记得上面提到的 NaN === NaN 为 false 吗?
一个正常的变量自己等于自己一定会是 true ,假如只要有一个变量自己等于自己却是 false 的话,那我们就可以推断这个变量一定是 NaN 了,这方法是不是相当神奇呢,以后也不用去写 isNaN 了
你以为的并不是你以为的
一开始笔者先来用个简单的练习让大家小试身手一下:
console.log('a' + 'b') // ?
console.log(3 + 5) // ?
相信大家看了应该都知道上面两个会输出 ab 以及 8 ,但这时候问题就来了,到底这个 + 是字符串的相加还是数字的相加呢?
由于 JavaScript 是个弱类型语言,因此在进行运算时都会先做类型转换的动作,假如转型后发现有字符串的话就会进行字符串的相加了,不然就是一般的数字相加。
相信看完上述的说明之后读者应该会觉得 + 这个运算符就是在做相加这件事情吧,但 JavaScript 有非常多看似很正常的运算符背后却不是做该运算符应该要做的动作,举例来说:
console.log(+ 3) // ?
看到这个应该第一时间会想因为 + 的左边无法辨识,所以转型也会出现 undefined 因此最终结果应该也要是 undefined 或 error 吧!但其实最后会输出 3,这边笔者先卖个关子,不讲为什么答案会是 3。
接下来我们再来依样画葫芦一下,读者可以猜猜看下面会输出什么出来:
console.log(+ 'a') // ?
有了上面提到的例子后再来看这个应该会直接反射猜出答案是‘a’ ,但其实答案是 NaN 。
其实刚刚的 + 3 中的 + 并不是拿来做相加的动作,这个 + 是 Number() 的简写,换句话说刚刚笔者所做的不管是 + 3 或 + 'a' 都是在做转型成数字的动作,所以 + 3 可以输出 3 并不是因为 0 + 3 而是因为 Number(3) 的关系,至于 + 'a' 因为本身并不是一个数字,所以转型后当然就变成 NaN 了。
打印出 banana
看到这个标题相信读者应该会觉得笔者怪怪的,怎么会问这种小儿科的问题呢?不就简单的 console.log('banana') 就好了。
其实笔者今天想讲的是如何运用旁门左道的方式来打打印出 banana 这个单字,这边会套用到上面提到的所有观念,也就是说可以利用 NaN 当分水岭把 banana 切成左半部的 ba 以及右半部的 a。
左半部跟右半部的写法相信读者应该不会有什么太多的问题,只要写 'b' + 'a' 以及 'a' 即可,至于 NaN 要怎么产出呢?还记得上面提到的 + 运算符吗?这边就是要利用这个观念来产出 NaN 啦!只要利用 + 'a' 就可以轻松的产出 NaN 了!
接下来就把上面提到的写法拼起来吧!
console.log('b' + 'a' + + 'a' + 'a') // baNaNa
这时候会发现输出的内容跟 banana 有一点落差,因为 NaN 的关系导致 n 变成大写了,所以这时候就必须要利用 toLowerCase() 这个方法把字符串内容全部变成小写,也就是像下面这样:
console.log(('b' + 'a' + + 'a' + 'a').toLowerCase()) // banana
小结
看完上述的内容有没有觉得 JavaScript 真的是个充满惊喜的程序语言呢?光一个 NaN 就可以搞死很多工程师,真不晓得为啥当初要设计成这样,难道是为了让后人有更多彩蛋可以挖吗