JavaScript 內(nèi)部,所有數(shù)字都是以64位浮點(diǎn)數(shù)形式儲(chǔ)存,即使整數(shù)也是如此。所以,1
與1.0
是相同的,是同一個(gè)數(shù)。
1 === 1.0 // true
這就是說,JavaScript 語(yǔ)言的底層根本沒有整數(shù),所有數(shù)字都是小數(shù)(64位浮點(diǎn)數(shù))。容易造成混淆的是,某些運(yùn)算只有整數(shù)才能完成,此時(shí) JavaScript 會(huì)自動(dòng)把64位浮點(diǎn)數(shù),轉(zhuǎn)成32位整數(shù),然后再進(jìn)行運(yùn)算,參見《運(yùn)算符》一章的“位運(yùn)算”部分。
由于浮點(diǎn)數(shù)不是精確的值,所以涉及小數(shù)的比較和運(yùn)算要特別小心。
0.1 + 0.2 === 0.3
// false
0.3 / 0.1
// 2.9999999999999996
(0.3 - 0.2) === (0.2 - 0.1)
// false
根據(jù)國(guó)際標(biāo)準(zhǔn) IEEE 754,JavaScript 浮點(diǎn)數(shù)的64個(gè)二進(jìn)制位,從最左邊開始,是這樣組成的。
0
表示正數(shù),1
表示負(fù)數(shù)符號(hào)位決定了一個(gè)數(shù)的正負(fù),指數(shù)部分決定了數(shù)值的大小,小數(shù)部分決定了數(shù)值的精度。
指數(shù)部分一共有11個(gè)二進(jìn)制位,因此大小范圍就是0到2047。IEEE 754 規(guī)定,如果指數(shù)部分的值在0到2047之間(不含兩個(gè)端點(diǎn)),那么有效數(shù)字的第一位默認(rèn)總是1,不保存在64位浮點(diǎn)數(shù)之中。也就是說,有效數(shù)字這時(shí)總是1.xx...xx
的形式,其中xx..xx
的部分保存在64位浮點(diǎn)數(shù)之中,最長(zhǎng)可能為52位。因此,JavaScript 提供的有效數(shù)字最長(zhǎng)為53個(gè)二進(jìn)制位。
(-1)^符號(hào)位 * 1.xx...xx * 2^指數(shù)部分
上面公式是正常情況下(指數(shù)部分在0到2047之間),一個(gè)數(shù)在 JavaScript 內(nèi)部實(shí)際的表示形式。
精度最多只能到53個(gè)二進(jìn)制位,這意味著,絕對(duì)值小于2的53次方的整數(shù),即-253到253,都可以精確表示。
Math.pow(2, 53)
// 9007199254740992
Math.pow(2, 53) + 1
// 9007199254740992
Math.pow(2, 53) + 2
// 9007199254740994
Math.pow(2, 53) + 3
// 9007199254740996
Math.pow(2, 53) + 4
// 9007199254740996
上面代碼中,大于2的53次方以后,整數(shù)運(yùn)算的結(jié)果開始出現(xiàn)錯(cuò)誤。所以,大于2的53次方的數(shù)值,都無法保持精度。由于2的53次方是一個(gè)16位的十進(jìn)制數(shù)值,所以簡(jiǎn)單的法則就是,JavaScript 對(duì)15位的十進(jìn)制數(shù)都可以精確處理。
Math.pow(2, 53)
// 9007199254740992
// 多出的三個(gè)有效數(shù)字,將無法保存
9007199254740992111
// 9007199254740992000
上面示例表明,大于2的53次方以后,多出來的有效數(shù)字(最后三位的111
)都會(huì)無法保存,變成0。
根據(jù)標(biāo)準(zhǔn),64位浮點(diǎn)數(shù)的指數(shù)部分的長(zhǎng)度是11個(gè)二進(jìn)制位,意味著指數(shù)部分的最大值是2047(2的11次方減1)。也就是說,64位浮點(diǎn)數(shù)的指數(shù)部分的值最大為2047,分出一半表示負(fù)數(shù),則 JavaScript 能夠表示的數(shù)值范圍為21024到2-1023(開區(qū)間),超出這個(gè)范圍的數(shù)無法表示。
如果一個(gè)數(shù)大于等于2的1024次方,那么就會(huì)發(fā)生“正向溢出”,即 JavaScript 無法表示這么大的數(shù),這時(shí)就會(huì)返回Infinity
。
Math.pow(2, 1024) // Infinity
如果一個(gè)數(shù)小于等于2的-1075次方(指數(shù)部分最小值-1023,再加上小數(shù)部分的52位),那么就會(huì)發(fā)生為“負(fù)向溢出”,即 JavaScript 無法表示這么小的數(shù),這時(shí)會(huì)直接返回0。
Math.pow(2, -1075) // 0
下面是一個(gè)實(shí)際的例子。
var x = 0.5;
for(var i = 0; i < 25; i++) {
x = x * x;
}
x // 0
上面代碼中,對(duì)0.5
連續(xù)做25次平方,由于最后結(jié)果太接近0,超出了可表示的范圍,JavaScript 就直接將其轉(zhuǎn)為0。
JavaScript 提供Number
對(duì)象的MAX_VALUE
和MIN_VALUE
屬性,返回可以表示的具體的最大值和最小值。
Number.MAX_VALUE // 1.7976931348623157e+308
Number.MIN_VALUE // 5e-324
JavaScript 的數(shù)值有多種表示方法,可以用字面形式直接表示,比如35
(十進(jìn)制)和0xFF
(十六進(jìn)制)。
數(shù)值也可以采用科學(xué)計(jì)數(shù)法表示,下面是幾個(gè)科學(xué)計(jì)數(shù)法的例子。
123e3 // 123000
123e-3 // 0.123
-3.1E+12
.1e-23
科學(xué)計(jì)數(shù)法允許字母e
或E
的后面,跟著一個(gè)整數(shù),表示這個(gè)數(shù)值的指數(shù)部分。
以下兩種情況,JavaScript 會(huì)自動(dòng)將數(shù)值轉(zhuǎn)為科學(xué)計(jì)數(shù)法表示,其他情況都采用字面形式直接表示。
(1)小數(shù)點(diǎn)前的數(shù)字多于21位。
1234567890123456789012
// 1.2345678901234568e+21
123456789012345678901
// 123456789012345680000
(2)小數(shù)點(diǎn)后的零多于5個(gè)。
// 小數(shù)點(diǎn)后緊跟5個(gè)以上的零,
// 就自動(dòng)轉(zhuǎn)為科學(xué)計(jì)數(shù)法
0.0000003 // 3e-7
// 否則,就保持原來的字面形式
0.000003 // 0.000003
使用字面量(literal)直接表示一個(gè)數(shù)值時(shí),JavaScript 對(duì)整數(shù)提供四種進(jìn)制的表示方法:十進(jìn)制、十六進(jìn)制、八進(jìn)制、二進(jìn)制。
0o
或0O
的數(shù)值,或者有前導(dǎo)0、且只用到0-7的八個(gè)阿拉伯?dāng)?shù)字的數(shù)值。0x
或0X
的數(shù)值。0b
或0B
的數(shù)值。
默認(rèn)情況下,JavaScript 內(nèi)部會(huì)自動(dòng)將八進(jìn)制、十六進(jìn)制、二進(jìn)制轉(zhuǎn)為十進(jìn)制。下面是一些例子。
0xff // 255
0o377 // 255
0b11 // 3
如果八進(jìn)制、十六進(jìn)制、二進(jìn)制的數(shù)值里面,出現(xiàn)不屬于該進(jìn)制的數(shù)字,就會(huì)報(bào)錯(cuò)。
0xzz // 報(bào)錯(cuò)
0o88 // 報(bào)錯(cuò)
0b22 // 報(bào)錯(cuò)
上面代碼中,十六進(jìn)制出現(xiàn)了字母z
、八進(jìn)制出現(xiàn)數(shù)字8
、二進(jìn)制出現(xiàn)數(shù)字2
,因此報(bào)錯(cuò)。
通常來說,有前導(dǎo)0的數(shù)值會(huì)被視為八進(jìn)制,但是如果前導(dǎo)0后面有數(shù)字8
和9
,則該數(shù)值被視為十進(jìn)制。
0888 // 888
0777 // 511
前導(dǎo)0表示八進(jìn)制,處理時(shí)很容易造成混亂。ES5 的嚴(yán)格模式和 ES6,已經(jīng)廢除了這種表示法,但是瀏覽器為了兼容以前的代碼,目前還繼續(xù)支持這種表示法。
JavaScript 提供了幾個(gè)特殊的數(shù)值。
前面說過,JavaScript 的64位浮點(diǎn)數(shù)之中,有一個(gè)二進(jìn)制位是符號(hào)位。這意味著,任何一個(gè)數(shù)都有一個(gè)對(duì)應(yīng)的負(fù)值,就連0
也不例外。
JavaScript 內(nèi)部實(shí)際上存在2個(gè)0
:一個(gè)是+0
,一個(gè)是-0
,區(qū)別就是64位浮點(diǎn)數(shù)表示法的符號(hào)位不同。它們是等價(jià)的。
-0 === +0 // true
0 === -0 // true
0 === +0 // true
幾乎所有場(chǎng)合,正零和負(fù)零都會(huì)被當(dāng)作正常的0
。
+0 // 0
-0 // 0
(-0).toString() // '0'
(+0).toString() // '0'
唯一有區(qū)別的場(chǎng)合是,+0
或-0
當(dāng)作分母,返回的值是不相等的。
(1 / +0) === (1 / -0) // false
上面的代碼之所以出現(xiàn)這樣結(jié)果,是因?yàn)槌哉愕玫?code>+Infinity,除以負(fù)零得到-Infinity
,這兩者是不相等的(關(guān)于Infinity
詳見下文)。
(1)含義
NaN
是 JavaScript 的特殊值,表示“非數(shù)字”(Not a Number),主要出現(xiàn)在將字符串解析成數(shù)字出錯(cuò)的場(chǎng)合。
5 - 'x' // NaN
上面代碼運(yùn)行時(shí),會(huì)自動(dòng)將字符串x
轉(zhuǎn)為數(shù)值,但是由于x
不是數(shù)值,所以最后得到結(jié)果為NaN
,表示它是“非數(shù)字”(NaN
)。
另外,一些數(shù)學(xué)函數(shù)的運(yùn)算結(jié)果會(huì)出現(xiàn)NaN
。
Math.acos(2) // NaN
Math.log(-1) // NaN
Math.sqrt(-1) // NaN
0
除以0
也會(huì)得到NaN
。
0 / 0 // NaN
需要注意的是,NaN
不是獨(dú)立的數(shù)據(jù)類型,而是一個(gè)特殊數(shù)值,它的數(shù)據(jù)類型依然屬于Number
,使用typeof
運(yùn)算符可以看得很清楚。
typeof NaN // 'number'
(2)運(yùn)算規(guī)則
NaN
不等于任何值,包括它本身。
NaN === NaN // false
數(shù)組的indexOf
方法內(nèi)部使用的是嚴(yán)格相等運(yùn)算符,所以該方法對(duì)NaN
不成立。
[NaN].indexOf(NaN) // -1
NaN
在布爾運(yùn)算時(shí)被當(dāng)作false
。
Boolean(NaN) // false
NaN
與任何數(shù)(包括它自己)的運(yùn)算,得到的都是NaN
。
NaN + 32 // NaN
NaN - 32 // NaN
NaN * 32 // NaN
NaN / 32 // NaN
(1)含義
Infinity
表示“無窮”,用來表示兩種場(chǎng)景。一種是一個(gè)正的數(shù)值太大,或一個(gè)負(fù)的數(shù)值太小,無法表示;另一種是非0數(shù)值除以0,得到Infinity
。
// 場(chǎng)景一
Math.pow(2, 1024)
// Infinity
// 場(chǎng)景二
0 / 0 // NaN
1 / 0 // Infinity
上面代碼中,第一個(gè)場(chǎng)景是一個(gè)表達(dá)式的計(jì)算結(jié)果太大,超出了能夠表示的范圍,因此返回Infinity
。第二個(gè)場(chǎng)景是0
除以0
會(huì)得到NaN
,而非0數(shù)值除以0
,會(huì)返回Infinity
。
Infinity
有正負(fù)之分,Infinity
表示正的無窮,-Infinity
表示負(fù)的無窮。
Infinity === -Infinity // false
1 / -0 // -Infinity
-1 / -0 // Infinity
上面代碼中,非零正數(shù)除以-0
,會(huì)得到-Infinity
,負(fù)數(shù)除以-0
,會(huì)得到Infinity
。
由于數(shù)值正向溢出(overflow)、負(fù)向溢出(underflow)和被0
除,JavaScript 都不報(bào)錯(cuò),所以單純的數(shù)學(xué)運(yùn)算幾乎沒有可能拋出錯(cuò)誤。
Infinity
大于一切數(shù)值(除了NaN
),-Infinity
小于一切數(shù)值(除了NaN
)。
Infinity > 1000 // true
-Infinity < -1000 // true
Infinity
與NaN
比較,總是返回false
。
Infinity > NaN // false
-Infinity > NaN // false
Infinity < NaN // false
-Infinity < NaN // false
(2)運(yùn)算規(guī)則
Infinity
的四則運(yùn)算,符合無窮的數(shù)學(xué)計(jì)算規(guī)則。
5 * Infinity // Infinity
5 - Infinity // -Infinity
Infinity / 5 // Infinity
5 / Infinity // 0
0乘以Infinity
,返回NaN
;0除以Infinity
,返回0
;Infinity
除以0,返回Infinity
。
0 * Infinity // NaN
0 / Infinity // 0
Infinity / 0 // Infinity
Infinity
加上或乘以Infinity
,返回的還是Infinity
。
Infinity + Infinity // Infinity
Infinity * Infinity // Infinity
Infinity
減去或除以Infinity
,得到NaN
。
Infinity - Infinity // NaN
Infinity / Infinity // NaN
Infinity
與null
計(jì)算時(shí),null
會(huì)轉(zhuǎn)成0,等同于與0
的計(jì)算。
null * Infinity // NaN
null / Infinity // 0
Infinity / null // Infinity
Infinity
與undefined
計(jì)算,返回的都是NaN
。
undefined + Infinity // NaN
undefined - Infinity // NaN
undefined * Infinity // NaN
undefined / Infinity // NaN
Infinity / undefined // NaN
(1)基本用法
parseInt
方法用于將字符串轉(zhuǎn)為整數(shù)。
parseInt('123') // 123
如果字符串頭部有空格,空格會(huì)被自動(dòng)去除。
parseInt(' 81') // 81
如果parseInt
的參數(shù)不是字符串,則會(huì)先轉(zhuǎn)為字符串再轉(zhuǎn)換。
parseInt(1.23) // 1
// 等同于
parseInt('1.23') // 1
字符串轉(zhuǎn)為整數(shù)的時(shí)候,是一個(gè)個(gè)字符依次轉(zhuǎn)換,如果遇到不能轉(zhuǎn)為數(shù)字的字符,就不再進(jìn)行下去,返回已經(jīng)轉(zhuǎn)好的部分。
parseInt('8a') // 8
parseInt('12**') // 12
parseInt('12.34') // 12
parseInt('15e2') // 15
parseInt('15px') // 15
上面代碼中,parseInt
的參數(shù)都是字符串,結(jié)果只返回字符串頭部可以轉(zhuǎn)為數(shù)字的部分。
如果字符串的第一個(gè)字符不能轉(zhuǎn)化為數(shù)字(后面跟著數(shù)字的正負(fù)號(hào)除外),返回NaN
。
parseInt('abc') // NaN
parseInt('.3') // NaN
parseInt('') // NaN
parseInt('+') // NaN
parseInt('+1') // 1
所以,parseInt
的返回值只有兩種可能,要么是一個(gè)十進(jìn)制整數(shù),要么是NaN
。
如果字符串以0x
或0X
開頭,parseInt
會(huì)將其按照十六進(jìn)制數(shù)解析。
parseInt('0x10') // 16
如果字符串以0
開頭,將其按照10進(jìn)制解析。
parseInt('011') // 11
對(duì)于那些會(huì)自動(dòng)轉(zhuǎn)為科學(xué)計(jì)數(shù)法的數(shù)字,parseInt
會(huì)將科學(xué)計(jì)數(shù)法的表示方法視為字符串,因此導(dǎo)致一些奇怪的結(jié)果。
parseInt(1000000000000000000000.5) // 1
// 等同于
parseInt('1e+21') // 1
parseInt(0.0000008) // 8
// 等同于
parseInt('8e-7') // 8
(2)進(jìn)制轉(zhuǎn)換
parseInt
方法還可以接受第二個(gè)參數(shù)(2到36之間),表示被解析的值的進(jìn)制,返回該值對(duì)應(yīng)的十進(jìn)制數(shù)。默認(rèn)情況下,parseInt
的第二個(gè)參數(shù)為10,即默認(rèn)是十進(jìn)制轉(zhuǎn)十進(jìn)制。
parseInt('1000') // 1000
// 等同于
parseInt('1000', 10) // 1000
下面是轉(zhuǎn)換指定進(jìn)制的數(shù)的例子。
parseInt('1000', 2) // 8
parseInt('1000', 6) // 216
parseInt('1000', 8) // 512
上面代碼中,二進(jìn)制、六進(jìn)制、八進(jìn)制的1000
,分別等于十進(jìn)制的8、216和512。這意味著,可以用parseInt
方法進(jìn)行進(jìn)制的轉(zhuǎn)換。
如果第二個(gè)參數(shù)不是數(shù)值,會(huì)被自動(dòng)轉(zhuǎn)為一個(gè)整數(shù)。這個(gè)整數(shù)只有在2到36之間,才能得到有意義的結(jié)果,超出這個(gè)范圍,則返回NaN
。如果第二個(gè)參數(shù)是0
、undefined
和null
,則直接忽略。
parseInt('10', 37) // NaN
parseInt('10', 1) // NaN
parseInt('10', 0) // 10
parseInt('10', null) // 10
parseInt('10', undefined) // 10
如果字符串包含對(duì)于指定進(jìn)制無意義的字符,則從最高位開始,只返回可以轉(zhuǎn)換的數(shù)值。如果最高位無法轉(zhuǎn)換,則直接返回NaN
。
parseInt('1546', 2) // 1
parseInt('546', 2) // NaN
上面代碼中,對(duì)于二進(jìn)制來說,1
是有意義的字符,5
、4
、6
都是無意義的字符,所以第一行返回1,第二行返回NaN
。
前面說過,如果parseInt
的第一個(gè)參數(shù)不是字符串,會(huì)被先轉(zhuǎn)為字符串。這會(huì)導(dǎo)致一些令人意外的結(jié)果。
parseInt(0x11, 36) // 43
parseInt(0x11, 2) // 1
// 等同于
parseInt(String(0x11), 36)
parseInt(String(0x11), 2)
// 等同于
parseInt('17', 36)
parseInt('17', 2)
上面代碼中,十六進(jìn)制的0x11
會(huì)被先轉(zhuǎn)為十進(jìn)制的17,再轉(zhuǎn)為字符串。然后,再用36進(jìn)制或二進(jìn)制解讀字符串17
,最后返回結(jié)果43
和1
。
這種處理方式,對(duì)于八進(jìn)制的前綴0,尤其需要注意。
parseInt(011, 2) // NaN
// 等同于
parseInt(String(011), 2)
// 等同于
parseInt(String(9), 2)
上面代碼中,第一行的011
會(huì)被先轉(zhuǎn)為字符串9
,因?yàn)?code>9不是二進(jìn)制的有效字符,所以返回NaN
。如果直接計(jì)算parseInt('011', 2)
,011
則是會(huì)被當(dāng)作二進(jìn)制處理,返回3。
JavaScript 不再允許將帶有前綴0的數(shù)字視為八進(jìn)制數(shù),而是要求忽略這個(gè)0
。但是,為了保證兼容性,大部分瀏覽器并沒有部署這一條規(guī)定。
parseFloat
方法用于將一個(gè)字符串轉(zhuǎn)為浮點(diǎn)數(shù)。
parseFloat('3.14') // 3.14
如果字符串符合科學(xué)計(jì)數(shù)法,則會(huì)進(jìn)行相應(yīng)的轉(zhuǎn)換。
parseFloat('314e-2') // 3.14
parseFloat('0.0314E+2') // 3.14
如果字符串包含不能轉(zhuǎn)為浮點(diǎn)數(shù)的字符,則不再進(jìn)行往后轉(zhuǎn)換,返回已經(jīng)轉(zhuǎn)好的部分。
parseFloat('3.14more non-digit characters') // 3.14
parseFloat
方法會(huì)自動(dòng)過濾字符串前導(dǎo)的空格。
parseFloat('\t\v\r12.34\n ') // 12.34
如果參數(shù)不是字符串,則會(huì)先轉(zhuǎn)為字符串再轉(zhuǎn)換。
parseFloat([1.23]) // 1.23
// 等同于
parseFloat(String([1.23])) // 1.23
如果字符串的第一個(gè)字符不能轉(zhuǎn)化為浮點(diǎn)數(shù),則返回NaN
。
parseFloat([]) // NaN
parseFloat('FF2') // NaN
parseFloat('') // NaN
上面代碼中,尤其值得注意,parseFloat
會(huì)將空字符串轉(zhuǎn)為NaN
。
這些特點(diǎn)使得parseFloat
的轉(zhuǎn)換結(jié)果不同于Number
函數(shù)。
parseFloat(true) // NaN
Number(true) // 1
parseFloat(null) // NaN
Number(null) // 0
parseFloat('') // NaN
Number('') // 0
parseFloat('123.45#') // 123.45
Number('123.45#') // NaN
isNaN
方法可以用來判斷一個(gè)值是否為NaN
。
isNaN(NaN) // true
isNaN(123) // false
但是,isNaN
只對(duì)數(shù)值有效,如果傳入其他值,會(huì)被先轉(zhuǎn)成數(shù)值。比如,傳入字符串的時(shí)候,字符串會(huì)被先轉(zhuǎn)成NaN
,所以最后返回true
,這一點(diǎn)要特別引起注意。也就是說,isNaN
為true
的值,有可能不是NaN
,而是一個(gè)字符串。
isNaN('Hello') // true
// 相當(dāng)于
isNaN(Number('Hello')) // true
出于同樣的原因,對(duì)于對(duì)象和數(shù)組,isNaN
也返回true
。
isNaN({}) // true
// 等同于
isNaN(Number({})) // true
isNaN(['xzy']) // true
// 等同于
isNaN(Number(['xzy'])) // true
但是,對(duì)于空數(shù)組和只有一個(gè)數(shù)值成員的數(shù)組,isNaN
返回false
。
isNaN([]) // false
isNaN([123]) // false
isNaN(['123']) // false
上面代碼之所以返回false
,原因是這些數(shù)組能被Number
函數(shù)轉(zhuǎn)成數(shù)值,請(qǐng)參見《數(shù)據(jù)類型轉(zhuǎn)換》一章。
因此,使用isNaN
之前,最好判斷一下數(shù)據(jù)類型。
function myIsNaN(value) {
return typeof value === 'number' && isNaN(value);
}
判斷NaN
更可靠的方法是,利用NaN
為唯一不等于自身的值的這個(gè)特點(diǎn),進(jìn)行判斷。
function myIsNaN(value) {
return value !== value;
}
isFinite
方法返回一個(gè)布爾值,表示某個(gè)值是否為正常的數(shù)值。
isFinite(Infinity) // false
isFinite(-Infinity) // false
isFinite(NaN) // false
isFinite(undefined) // false
isFinite(null) // true
isFinite(-1) // true
除了Infinity
、-Infinity
、NaN
和undefined
這幾個(gè)值會(huì)返回false
,isFinite
對(duì)于其他的數(shù)值都會(huì)返回true
。
更多建議: