跟著 YDKJS 作者 Kyle Simpson 打造全新 JavaScript Mindset : 特殊值(undefined , NaN , Null)

Ashe_Li
Ashe’s Note
Published in
6 min readJul 24, 2020

--

什麼是 undefined ?

Javascript 試圖假裝「宣告不是很重要」,你可以有其他權宜之計(work around it)
Kyle Simpson 認為,這個部分不應該這樣做,應該 returned a string undeclared.

undeclared 表示它還沒有在 作用域(scope) 內建立

undefined 表示我們宣告一個變數,不過在這時候還 沒有賦值(no value).

很可惜,我們沒有 undeclared ,所以大家對這個值常常混淆。

常見的 var x 「預設值是 undefined」 。

如果可以
javascript 一定要宣告,那這樣的話沒有宣告的變數就是 Undeclared,代表「變數沒有被建立」。

ES6之後,一個狀態 uninitialized ,「存在」有一個變數但是「還沒有被初始化」,這時候大家都不能碰,否則就是TDZ (temporal dead zone) Error。

這是我自己理解畫的圖:

因為 Undeclared 不存在,如果在非嚴格模式底下,JS 會自作聰明的幫你宣告。
順帶一提,「宣告」這件事,有關 hoisting ,未來文章有機會介紹。

真正來說,undefined 就是個普通的原始值,跟 2 或者 "hello" 是一樣的。
只不過使用時要謹慎。

undefinednull只有一個且唯一的「值」。
然而,null 也是個騙子。
由於 JavaScript 中的一個 bug,它會被識別成一個物件。

console.log(typeof(null)); // “object” (騙人啊!)

你可能在玩 JS踩地雷 或是 JS面試大補帖 之類的小遊戲(?),
對 null 的坑可能就有所了解。

早期我使用 undefined 當作 reset 一個變數的方法
目前我比較多使用 null, 可以避免和 原始值 的語義混淆。

NaN — isNaN

之前社群有人寫過這篇文章,我鐵人賽也寫過,直接搬過來~

來自 IEEE 754 規範。 IEEE 754 定義 (NaN ≠ x) = false.
不要理解成 Not a Number 的縮寫,理解成
Invalid number

要理解 NaN ,應該想成 NaN 是一個 警戒值(sentinel values),表示 Invalid number 數字無效的狀態。

*註:警戒值(sentinel values): 比如迴圈的終止條件寫 -1,這時候不是真的有一個值會是 -1,只是一個用來判斷終止條件的 狀態

為甚麼會說 Invalid number 比起 Not a Number 是更好的解釋?

下面列出幾種理由。

1. typeof NaN === “number”

這是最常見的問題,因為如果解釋成 Not a Number ,很容易和真實語意不符,造成誤解。
Invalid number 可以從字面上解釋解釋,因為 Invalid number 還是 number。

  • 註:從定義解釋
    (IEEE 754 spec which is a numeric representation specification.)
    IEEE 754 是專門 規範 numeric 的文件,當然「型別」還是 number,只是其「值」是一個特殊狀態

NaN 有參與的任何 numeric 運算(比如字串減法),結果都是 NaN。

這個問題,思考的核心是 Invalid number 的運算是沒意義的(Invalid),
自然就回傳 Invalid number,也就是這邊的 Invalid 代表 沒意義
如果解釋成 Not a Number ,很難理解關聯。

  • Invalid number 與任何數字運算,整個運算式也仍然表示 Invalid ( Invalid 狀態 a.k.a. 警戒值)
  • 常見於 減法運算,subtraction operator(-) 限定數字運算,所以會強制轉型成 numbers,不像加法有運算子多載 (可以做字串運算)

NaN === NaN

// false

順帶一提,整個 js 只有這組特殊值 自己不等於自己

IEEE_754 規範 spec 明確指出 :
NaN 與任何浮點數(包括自身)的比較結果都為假,即 (NaN ≠ x) = false.

isNaN

isNaN(some var) 。var 在比對以前,也會強制轉型成數字,所以字串被轉型成 NaN

很顯然強制轉型是一個爛想法,所以 ES6 以後可以透過 Number.isNaN 做不轉型的比對。

關於 Kyle Simpson 思想

一般來說,IEEE 754 回傳拿到 NaN 是在純數字運算
但是如果發生一種狀況:數字運算,結果回傳不是數字,所以才會設計回傳 Invalid number a.k.a. NaN

很顯然這種狀況對不知道 數字運算,結果回傳不是數字要怎麼思考的人,造成很大困難。

有人就抱怨:

不應該存在 或 不應該使用

NaN 當作回傳 !

延伸這個問題:那麼你要設計什麼回傳?

如果今天沒有 NaN,那今天遇到不是數字(或是 Invalid number)的回傳,可以回傳什麼?

  • 回傳 undefined / null / false :
    如果數字運算,忽然變成回傳其他型別,你一定會後悔。

你可能想,「ok, 那我設計回傳數字!」

  • 回傳 -1 :
    基於歷史原因,比如 Array 中的遍歷,失敗時會回傳 -1。
    但回傳 -1 是因為 C語言 時期 40 多年以前還沒有 IEEE 的標準,才逼不得已。
    所以 你要放棄 IEEE 的標準 ,造成更多問題嗎? (為何實作一半)
  • 回傳 0 :
    你會很容易遇到強制轉型問題,
    而且語意來說,0 是有意義不是 沒有回傳

所以,如果今天你設計系統,改寫使用自定義非 NaN 的回傳時,請仔細測試它,否則你會產生其他 bug。

--

--