[JavaScript]深入解析 NaN 和 isNaN

什麼是NaN?

NaN(Not a Number, 非數字), NaN 屬性的初始值就是 NaN, 和 Number.NaN 的值一樣 NaN 屬性是一個不可配置(non-configurable), 不可寫(non-writable)的屬性. 但在ES3中, 這個屬性的值是可以被更改的, 但應該避免被覆蓋.

**注意**: 雖然NaN本身名字爲非數字,但是利用typeof來判斷NaN的時候, 卻會返回number.

NaN的產生

一般編碼中很少會直接出現或使用NaN, 通常通常都是在計算失敗[1]時, 或者嘗試將一個字符串解析轉換[2]成數字但失敗了的時候; 任何涉及與NaN的計算[3]時, 返回結果都爲NaN.

[1]   計算失敗時:
對於未定義的運算(Infinity相關)或錯誤數學公式計算時(負數進行開偶次方; 負數進行對數運算; 小於-1或大於+1的數進行反正弦或反餘弦運算), 得到的計算結果爲 NaN
document.write(Math.sqrt(-1));      // 結果爲 NaN
document.write(0/0);                // 結果爲 NaN
document.write(5/0);                // 結果爲 Infinity
document.write(Infinity/Infinity)   // 結果爲 NaN
document.write(0/Infinity);         // 結果爲 0
document.write(0*Infinity);         // 結果爲 NaN
進行對 加法(+) 以外的四則運算時, 即 對帶有減號 (-) 乘號 (*) 或 除號 (/) 運算符時, JavaScript引擎會先嚐試將待計算單項轉換成Number類型(使用 Number(x) 做轉換), 如果轉換失敗, 整個計算結果即爲 NaN
10 - '5a'           // NaN
'5a' * 5            // NaN
'10' / '5a';        // NaN

undefined - 5;      // NaN  Number(undefined) == NaN
'' * 5              // 0    Number('') == 0
true * 5            // 5    Number(true) == 1
[] * 5              // 0    Number([]) == 0
null - 5;           // -5   Number(null) == 0
對於四則運算 加法(+)時, JavaScript引擎不會對單項先進行Number強制轉換, 如果加號(+)兩端單項 有任意一個是字符串 js會進行拼接操作, 只有加號(+) 兩端單項都是數字Number類型的時, 纔會進行相加計算.  
document.write(1 + 2 + '3'         + "<br/>");    // 計算結果爲 33
document.write(1 + '2' + 3         + "<br/>");    // 計算結果爲 123
document.write(1 + undefined + 3   + "<br/>");    // 計算結果爲 NaN
document.write(1 + '' + 3          + "<br/>");    // 計算結果爲 13
[2]   解析數字失敗時, 任何一個字符串當不能被 parseInt, parseFloat 或 Number 成功轉換時, 就返回 NaN, 表示該字符串無法被識別爲Number類型, 這是一個異常狀態, 並不是一個確切的值 :
當被轉換值中不包含任何數字時, 使用parseInt, parseFloat 或 Number進行轉換時, 直接返回 NaN
parseInt('bluetata')     // NaN
parseFloat('bluetata')   // NaN
Number('bluetata')       // NaN
當被轉換值中含任何數字時, parseInt 和 parseFloat 會將被轉換值中第一個無效字符之前的 數字字符串進行轉換; Number 會將被轉換值當做一個整體來轉換, 這樣對於Number()來說, 即使被轉換值中包含數字, 也不會將數字轉換出來. 
parseInt('123abc')         // 轉換結果 123
parseInt('123abc45')       // 轉換結果 123
parseInt('abc123def')      // 轉換結果 NaN
parseFloat('123.45abc')    // 轉換結果 123.45
parseFloat('123abc')       // 轉換結果 123
Number('123abc')           // 轉換結果 NaN
[3]   與 NaN 進行計算時:

任何涉及與NaN的計算, 返回結果既爲NaN

    document.write("NaN === NaN     : " + (NaN === NaN)   + "<br/>");             // false
    
    document.write("isNaN(NaN + 10) : " + isNaN(NaN + 10) + "<br/>");             // true
    document.write("isNaN(NaN - 10) : " + isNaN(NaN - 10) + "<br/>");             // true
    document.write("isNaN(NaN * 10) : " + isNaN(NaN * 10) + "<br/>");             // true
    document.write("isNaN(0/NaN)    : " + isNaN(0/NaN)    + "<br/>");             // true
    document.write("isNaN(NaN/1)    : " + isNaN(NaN/1)    + "<br/>");             // true

深入理解isNaN

isNaN:  NaN不能通過相等操作符(== 和 ===)來判斷, 因爲 NaN 不與任何值相等, 即使是NaN自己本身. 因此, 對於如何判斷一個值是否爲NaN, 函數isNaN 的產生就很有必要了.

isNaN的判斷方法可以看做爲:

isNaN = function(value) {
    Number.isNaN(Number(value));
}

如果isNaN函數的參數不是Number類型, isNaN函數會首先嚐試將這個參數轉換爲數值, 然後纔會對轉換後的結果是否是NaN進行判斷.
因此, 對於能被強制轉換爲有效的非NaN數值來說(空字符串布爾值分別會被強制轉換爲數值01), 返回false值也許會讓人感覺莫名其妙. 比如說,空字符串就明顯"不是數值(not a number)". 這種怪異行爲起源於:"不是數值(not a number)" 在基於IEEE-754數值的浮點計算體制中代表了一種特定的含義. isNaN函數其實等同於回答了這樣一個問題:被測試的值(參數)在被強制轉換成數值時會不會返回IEEE-754​中所謂的"不是數值(not a number)".--同步於MDN中文翻譯「Revision 1372843 of isNaN() trans. by bluetata」

根據上述MDN的解釋, 可以看出 isNaN 本身實際需要將參數轉換成 Number 纔可以判斷其值是否爲 NaN, 本身並沒有能力判斷一個值是否爲 NaN ,所以可以利用 NaN 本身不等於自身 這一特性(因爲本身NaN不能通過===來判斷相等)來判斷其變量x是否爲NaN.

判斷一個變量是否真的是NaN判斷方法舉例如下:
function reallyIsNaN(x){
    return x !== x
}

爲了方便理解上述的判斷方法, 以下舉例說明了 其利用!==判斷式的相關結果:

var xVar;               // undefined
xVar !== xVar           // false 自身等於自身

var xVar = "bluetata";
xVar !== xVar           // false 自身等於自身

var xVar = NaN
xVar !== xVar           // true  自身不等於自身
在ECMAScript6之後, 判斷一個值是否爲NaN, 可以利用Number提供的isNaN進行判斷
Number.isNaN(x)

一些特殊的NaN判斷結果

isNaN(NaN);                 // true
isNaN(undefined);           // true
isNaN({});                  // true

isNaN(Infinity)             // false
isNaN(5/0)                  // false  5/0返回Infinity 認爲是無限大數字
isNaN(0/0)                  // true   爲NaN
isNaN("0/0")                // true   爲NaN
isNaN(Infinity/Infinity);   // true

isNaN(true);                // false: true被轉換成1 同樣如果是'false'會被轉換成0
isNaN(null);                // false  null被轉換成0
isNaN(37);                  // false

// strings
isNaN("37");                // false: 可以被轉換成數值37
isNaN("37.37");             // false: 可以被轉換成數值37.37
isNaN("");                  // false: 空字符串被轉換成0
isNaN(" ");                 // false: 包含空格的字符串被轉換成0
isNaN("bluetata")           // true: "blabla"不能轉換成數值

// dates
isNaN(new Date());                // false 時間會被轉換成毫秒 + 1
isNaN(new Date().toString());     // true

本文原創由`bluetata`發佈於blog.csdn.net、轉載請務必註明出處。


Flag Counter

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章