JavaScript 有趣的冷知識 :神奇的 NaN

Andy Chen
Andy的技術分享blog
7 min readJun 22, 2020

前言

隨著筆者寫 JavaScript 的時間越來越長,遇到的坑也越來越多😂,在踩坑的過程中也發現了一些有趣的冷知識,因此筆者就想寫成一個系列文來記錄這些有趣的冷知識,廢話不多說馬上開始今天的文章吧!

NaN

首先要介紹的是在 JavaScript 中讓人又愛又恨的 NaNNaN 的全名為:Not a Number,翻成中文就是這個值並不是一個數字,通常都會發生在進行數字的轉換上會回傳的結果,例如以下這段程式碼:

const a = 'abc123'
console.log(parseInt(a)) // NaN

大家都知道 JavaScript 是一個弱型別的語言,所以當今天需要確認此變數為什麼型態時,必須要用 typeof 這個 method 來判斷,接下來筆者就有個小問題要考考大家了,請大家猜猜看底下的程式碼會回傳什麼型態。

typeof NaN   // ?

答案揭曉!那就是 number

所以 NaN 用白話文翻譯來說就是:本身是數字型態但卻不是數字的數字。

其實也不難想像為什麼 JavaScript 會這樣定義,回到筆者上面說的 NaN 通常都是發生在要轉換成數字時發現不能用任何一個數字來表現,由於本身經過轉換已經是數字的型態了,也因此 NaN 的型態才會是 number

接下來再考考大家下面這段判斷式會回傳 true 還是 false。

NaN === NaN  

答案是回傳 false。

其實早在 IEEE 754 的規範中就有定義如果判斷式遇到 NaN 就要回傳 false,但應該大多數的讀者跟筆者一樣都不會刻意去看這種文件吧XD,這時候就可以多看看 ECMAScript 的規範,裡面也會提到一些跟 NaN 有關的比較說明,這樣也可以避免踩到非常多的地雷XD

判斷 NaN

這時候你可能會問那我要怎麼判斷是不是 NaN 呢?方法也很簡單,JavaScript 自己有提供一套 method 叫 isNaN() 透過這個 method 很快就可以知道是不是 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() 的數值轉換,因此最終結果也比較符合大家所設想的結果。

但除了上述的技巧外,也有一個非常 tricky 的方式可以檢查 NaN,還記得上面提到的 NaN === NaNfalse 嗎?

今天一個正常的變數自己等於自己一定會是 true,假如今天只要有一個變數自己等於自己卻是 false 的話,那我們就可以推斷這個變數一定是 NaN 了,這方法是不是相當神奇呢,以後也不用去寫 isNaN 了XD

你以為的並不是你以為的

一開始筆者先來用個簡單的練習讓大家小試身手一下:

console.log('a' + 'b')   // ?
console.log(3 + 5) // ?

相信大家看了應該都知道上面兩個會輸出 ab 以及 8 ,但這時候問題就來了,到底這個 + 是字串的相加還是數字的相加呢?

由於 JavaScript 是個弱型別語言,因此在進行運算時都會先做型別轉換的動作,假如轉型後發現有字串的話就會進行字串的相加了,不然就是一般的數字相加。

相信看完上述的說明之後讀者應該會覺得 + 這個運算子就是在做相加這件事情吧,但 JavaScript 有非常多看似很正常的運算子背後卻不是做該運算子應該要做的動作,舉例來說:

console.log(+ 3)   // ?

看到這個應該第一時間會想因為 + 的左邊無法辨識,所以轉型也會出現 undefined 因此最終結果應該也要是 undefinederror 吧!但其實最後會輸出 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 就可以搞死很多工程師,真不曉得為啥當初要設計成這樣,難道是為了讓後人有更多彩蛋可以挖嗎XD

接下來筆者還是會繼續介紹其他有趣的冷知識,但切記學起來後不要隨便運用在職場上,不然背後可能就會有莫名的視線在盯著你看了🤣

--

--

Andy Chen
Andy的技術分享blog

嗨嗨我是Andy,用嘴巴工作的工程師😂,喜歡學習不同領域的內容,專長為網頁開發,歡迎大家跟我聊技術~