這篇主要概述JavaScript基本型別的意思,並針對講解數字這個型別。明天才會繼續一併講完字串、布林、undefined
、null
、symbol
。
在進入正題前,先看看JavaScript有什麼資料型別:
基本型別(Primitive data type)
物件型別(Object)
注意只有變數的值才會有型別,變數本身並不會有型別之分。
另外,基本型別的資料是不可改變(immutable),意思是我們不能更改屬於基本型別的值。如果我們要更改它的值,只能為該變數賦予一個新的基本型別的值。相反,物件型別是可以被修改的,不一定要重新賦予。
我們可以用typeof
去查詢該值是屬於什麼型別:
typeof(10) //number
typeof('10') //string
typeof(true) //boolean
typeof({}) //object
typeof([]) //object
typeof(null) //object
typeof(undefined) //undefined
奇怪,null
是屬於object
?是的,有些開發者認為這是JS本身一直存在的bug。因為這個bug已經存在好多年,如果改掉了就會使以前建好的網站出現問題,所以索性將錯就錯就好了。
JavaScript是屬於動態語言和弱型別語言。
var x = 10;
x = '10';
console.log(x); //10
console.log(typeof(x)); //string
var y = 10 + '1';
console.log(y); //101
聽起來很靈活方便,但如果沒留意好型別轉換就好容易藏下bug。例如我曾經在撈取伺服器回傳的資料時,直接拿仍然是字串型別的數字去做運算,結果就計算出奇奇怪怪的數字,但因為當時console
沒有報錯,菜鳥的我沒有察覺到哪裏出問題,還花了一點時間去找。
就是整數或帶有小數點的數字,例如: 1、1.1。
包括NaN
、Infinity
、-Infinity
。它們雖然不是一個數字,但在JavaScript的世界裏,它們是屬於數字(numbers)這個型別。
Infinity
是指數學上的無限大-Infinity
是指數學上的負無限大例如以下的運算會得出Infinity
:
NaN
(Not a number),MDN的解釋是:這個值不是一個數字。但有些開發者覺得這個解釋模糊,他們主張更清晰的解說,例如引用You Don't Know JS這本書中的解釋:It would be much more accurate to think of NaN as being “invalid number,” “failed number,” or even “bad number,” than to think of it as “not a number.”
NaN is a kind of “sentinel value” (an otherwise normal value that’s assigned a special meaning) that represents a special kind of error condition within the number set. The error condition is, in essence: “I tried to perform a mathematic operation but failed, so here’s the failed number result instead.”
重點就是:NaN
是指一個無效的數字,一個錯誤的狀態。這個錯誤狀態源於JavaScript嘗試執行一個數學運算,但結果失敗,所以只能得出一個無效的數值。
到底在什麼情況下會出現NaN?
MDN舉了幾個例子:
截圖自MDN
總括來說,情況包括:把本來不能轉型為數字的值強行轉為數字(e.g把字串轉成整數)、計算失敗(e.g -1平方根)、把NaN放入運算等等。
要注意的是,NaN
不等於任何數字,也不等於NaN
它自己
如何判斷該變數是否NaN?
傳統的做法就是把變數放入isNaN()
裏檢查,看看該變數是否NaN
但isNaN()
一直存在誤判的問題,舉個例子:
明明是一個字串,但isNaN()
就判斷它是true
。可是,我們知道字串就是字串,為什麼是true
?。
這個問題是因為isNaN
會先把()裏的值轉型成數值,如果不能轉成數值,這個值就是NaN
了。
在'hello world'
這個例子裏呢?
因為如果強行把'hello world'
轉成數值,會得出NaN
,因此會變成isNaN(NaN)
,而答案就變成了true
。
用Number()
去看'hello world'
,顯示NaN
Number('hello world') //NaN
為了解決先轉型做數字而導致誤判的這個問題,ES6提出了使用Number.isNaN()
的建議。以下是ES6官方文檔的解釋:
截圖自ECMAScript® 2015 Language Specification
由此可見,Number.isNaN()
不會事先把()裏的值轉為數值,而是判斷該值是否一個數值,因此避免了剛才誤判的問題。回到剛才'hello world'的例子,這一次終於顯示false
Number.isNaN('hello world') //false
JavaScript並不能精準讀取所有小數點的數字
先看看以下的運算:
剛學JS的我被結果嚇到。
首先,有兩個重點要了解:
想了解更多關於JavaScript所使用的IEEE 754可看這個YouTube影片,雖然後面code的部分我沒跟上XD,但前部分的解說挺易懂的~
以這個例子為例:0.1+0.2 = 0.30000000000000004
電腦會把十進位數字變成二進位:
0.1 = 0.0001100110011001101...(無限循環)
0.2 = 0.00110011001100110011...(無限循環)
0.1和0.2,在二進制的世界裏,是一個循環小數,原理就像平時我們1/3時,會有不斷循環的小數點數值。基於IEEE 754原則,JavaScript在儲存每一個數字時,並不會把該數字所有小數點都精準地保留起來。因為這裏只有64位(64bits)給它放數字,所以在過程中,它一定要把捨棄掉某些小數點,導致它最後不能精確還原原本的十進位數值,只能是盡量貼近。
例如0.1和0.2,即使有超多的小數在後面,也要把一些捨棄掉。
但為什麼這個例子又沒事呢?0.1+0.3 = 0.4
我嘗試用二進制計算機相加0.1和0.3的二進制數字,發現正正是等於0.4的二進制數字,所以就解釋到為什麼會順利得出0.4這個結果。
0.4的二進制:0.0110011001100110011
0.1+0.3的二進制相加(如下圖):
0.0001100110011001101 + 0.01001100110011001101
= 0.011001100110011
為了避免這個誤差問題,最直接的方法是把數字轉為整數才去作其他處理,但對於大數的處理,比較推薦用JS函式庫,例如number precision、math.js等等去作處理。
數字:
NaN
是指一個無效的數字,錯誤的狀態Number.isNaN()
比isNaN()
更精準找出該值是否NaN
想不到數字這個基本型別都寫了差不多一篇,明天會繼續把其餘的(字串、布林、null
等等)都一併講完,感謝你的閱讀!
弱型別、動態語言
弱类型、强类型、动态类型、静态类型语言的区别是什么?
你不可不知的 JavaScript 二三事#Day2:資料型態的夢魘——動態型別加弱型別(1)
isNaN問題
Better NaN check with ES6 Number.isNaN
从 Number.isNaN 与 isNaN 的区别说起
二進位浮點數算術標準的問題
重新認識 JavaScript: Day 03 變數與資料型別
0.1 + 0.2不等于0.3?为什么JavaScript有这种“骚”操作?
請問一下:
isNaN("Hello") -> True(hello非數值)
這應該沒什麼不對吧?
Number.isNaN("Hello"); -> False(這邊我才搞不懂為什麼?)