一蓄诽、背景
事情經(jīng)過是這樣的薛训,前幾天上班路上,跟往常一樣拿起手機看頭條仑氛、逛知乎乙埃、刷掘金嘛。
過程中锯岖,看到以下這個面試題:
['1', '2', '3'].map(parseInt)
輸出什么介袜?
其實,這題很簡單出吹,不就是類似 [0, 1, 2].filter(Boolean)
這種變形題目嘛遇伞,但我卻沒能馬上說出答案。
我知道 parseInt()
的第二個參數(shù)跟進制數(shù)相關(guān)捶牢,但由于平常多數(shù)是缺省第二個參數(shù)鸠珠,平常寫項目也會啟用 ESLint 的 radix 規(guī)則,但規(guī)則啟用時也幾乎是填寫 10
作為實參秋麸,因為涉及其他進制數(shù)的情況確實很少很少...
所以渐排,趁機再熟悉下 parseInt(string, radix)
這個函數(shù),也是挺不錯的竹勉。
回到上面的題目飞盆,分解一下,就是返回以下三個運算結(jié)果組成的數(shù)組嘛:
parseInt('1', 0, ['1', '2', '3'])
parseInt('2', 1, ['1', '2', '3'])
parseInt('3', 2, ['1', '2', '3'])
對于 parseInt()
函數(shù),僅接收兩個參數(shù)吓歇,所以第三個參數(shù)實際上沒有任何作用孽水,因此 ['1', '2', '3'].map(parseInt)
結(jié)果就是:
[
parseInt('1', 0),
parseInt('2', 1),
parseInt('3', 2)
]
但這篇文章的重點并非是答案,我們應(yīng)該關(guān)注 parseInt(string, radix) 函數(shù)本身城看。
二女气、八進制數(shù)表示法的前世今生
如果常用 ESLint 的同學(xué),應(yīng)該知道它有一個規(guī)則 radix 是跟 parseInt()
相關(guān)的测柠。
看個例子炼鞠,它們分別打印什么結(jié)果?
parseInt('071') // 57
parseInt('071', 10) // 71
有些本著求真的同學(xué)轰胁,將
parseInt('071')
拷到控制臺發(fā)現(xiàn)谒主,無論是 Chrome、Firefox 還是 Safari 都是打印出71
而不是57
赃阀。
我為什么寫成 57
呢霎肯?是寫錯了嗎?明明在瀏覽器中 parseInt('071')
都是打印出 71
呢榛斯!
先別急观游,我們知道在「嚴格模式」下,是不允許使用以 0
開頭的八進制語法的驮俗。
"use strict"
var n = 071 // SyntaxError: Octal literals are not allowed in strict mode.
但我想懂缕,你有可能不知道的是,其實在 ES6 標準發(fā)布之前王凑,ECMAScript 是沒有八進制語法的搪柑,至于類似 071
這種八進制表示法它只是被所有瀏覽器廠商支持罷了。就好比如 Object.prototype.__proto__
從來就不是 ECMAScript 的標準語法一樣索烹,但所有瀏覽器都支持罷了拌屏,標準語法是 Object.getPrototypeOf()
。
在 ES6 中提供了八進制數(shù)的標準規(guī)范:在數(shù)字前加上
0o
來表示八進制數(shù)术荤,比如八進制的71
用0o71
表示。
回到 parseInt(string, radix)
與八進制的話題上每篷,
當沒有指定
radix
參數(shù)時瓣戚,看看各家是如何解析八進制數(shù)的?
- ES3「不提倡」將帶有
0
開頭的數(shù)值字符串解析為一個八進制焦读。(不贊成子库,但沒禁止)- ES5 規(guī)范中「不允許」
parseInt
函數(shù)的實現(xiàn)環(huán)境把帶有0
開頭的數(shù)值字符串解析為八進制數(shù)值,而是以10
為基數(shù)(即十進制)進行解析矗晃。(規(guī)范禁止了仑嗅,但瀏覽器沒有按標準實現(xiàn))- 各瀏覽器廠商大爺們:我偏不按你規(guī)范去實現(xiàn),仍要把帶有
0
開頭的數(shù)值字符串解析成八進制數(shù)值。(我行我素)
本著求真的態(tài)度仓技,拿出了上古神器去驗證并得出結(jié)果:在 IE8 及以下瀏覽器 parseInt('071')
打印結(jié)果為 57
(下圖)鸵贬,而 IE9 及以上則為 71
。
隨著 JavaScript 的飛速發(fā)展脖捻,瀏覽器廠商們都向標準靠近了阔逼,不再肆意我行我素了。至于瀏覽器 parseInt('071')
打印結(jié)果是 71
地沮,原因正是現(xiàn)在的瀏覽器 JS 引擎是以 10
為基數(shù)進行解析了嗜浮。
盡管 2022 年了,但仍要兼容舊版(遠古)瀏覽器摩疑,所以顯式指定 radix
參數(shù)是非常有必要的危融。本節(jié)用一個比較典型的案例來說明,使用 parseInt
函數(shù)時雷袋,應(yīng)當指定 radix
參數(shù)吉殃。
在 JavaScript 中,有四種進制數(shù)的表示語法:
- 十進制:沒有「前導(dǎo)零」的數(shù)值片排。
- 二進制:以
0b
或0B
開頭的數(shù)值寨腔。- 八進制:以
0o
或0O
開頭的數(shù)值。瀏覽器等宿主環(huán)境也支持以「前導(dǎo)零」開頭率寡,且只有0 ~ 7
的數(shù)組表示八進制數(shù)迫卢。- 十六進制:以
0x
或0X
開頭的數(shù)值。
三冶共、parseInt
語法
parseInt(string, radix)
解析一個字符串并返回指定基數(shù)的「十進制整數(shù)」或者 NaN
乾蛤。
string
被解析的值。若參數(shù)不是字符串類型捅僵,內(nèi)部先隱式類型轉(zhuǎn)換為字符串類型(即調(diào)用相應(yīng)值的toString()
方法)radix
(可選家卖,取值范圍2 ~ 36
的整數(shù))
表示字符串的基數(shù)。但請注意庙楚,10
不是默認值上荡!當參數(shù)radix
缺省時,會有幾種情況馒闷,下面會介紹酪捡。
注意點
- 若
string
參數(shù)帶有前導(dǎo)或尾隨空白符(包括\n
、\r
纳账、\f
逛薇、\t
、\v
和空格)疏虫,它們將會被忽略永罚。換句話說啤呼,實際有意義的是第一個非空白符開始。
parseInt(' \n\r\f\t\v 11', 2) // 3呢袱,相當于 parseInt('11', 2)
- 當
string
參數(shù)的「第一個非空格字符」不能轉(zhuǎn)換為數(shù)字官扣,或者當radix < 2 || radix > 36
時,返回值為NaN
产捞。
parseInt('a11') // NaN
parseInt('11', 1) // NaN
parseInt('11', 37) // NaN
但請注意醇锚,并不是所有的字母開頭的都返回 NaN
。比如 parseInt('a11', 12)
返回值為 1453
坯临。因為超過十進制之后焊唬,字母也可能用于表示相應(yīng)的進制數(shù)的。
-
當
radix
參數(shù)為undefined
看靠、null
赶促、0
或缺省(未顯式指定)時挟炬,JavaScript 引擎會假定以下情況:- 如果
string
是以0x
或0X
開頭的鸥滨,那么radix
將會假定為16
,將其余部分當作十六進制數(shù)去解析谤祖。比如parseInt('0xf')
相當于parseInt('f', 16)
婿滓,結(jié)果為15
。 - 如果
string
是以0
開頭的粥喜,那么radix
在不同的環(huán)境下凸主,有可能被假定為8
(八進制)或假定為10
(十進制)。具體選擇哪一種作為radix
的值额湘,視乎運行 JavaScript 的宿主環(huán)境(前面提到過了)卿吐。因此,在使用parseInt
時锋华,一定要顯式指定radix
參數(shù)嗡官。 - 如何
string
是以任何其他值開頭,radix
會被假定為10
(十進制)毯焕。
- 如果
parseInt
對string
的解析規(guī)則是:從第一個非空白符開始衍腥,然后一直往后面查找,若有任意一個字符不能被轉(zhuǎn)換為數(shù)值就會停止纳猫,那么最終被解析的字符串就是從開始到停止的這一截字符串紧阔。
parseInt('11a', 10) // 結(jié)果為 11,被解析的字符串為 '11'
parseInt('11a', 11) // 結(jié)果為 142续担,被解析的字符串為 '11a',因為在十一進制里面活孩,a 是有意義的物遇。
所以,在使用 parseInt
處理 BigInt
類型的時候,最終的返回值總是為 Number
類型(過程中會失去精度)询兴,其中 BigInt
類型的拖尾的 n
是會被丟棄的乃沙。
parseInt(1024n, 10) // 1024
parseInt(1024n, 36) // 46732,相當于 parseInt('1024', 36)
// 注意诗舰,有別于以下這個
parseInt('1024n', 36) // 1682375
原因非常的簡單警儒,前面也提到過的。當 parseInt
的第一個參數(shù)不是 String
類型時眶根,會調(diào)用 BigInt.prototype.toString()
方法先轉(zhuǎn)換為字符串蜀铲,即 1024n.toString()
,結(jié)果為 1024
属百。
四记劝、結(jié)尾
回到文章開頭的題目:
[
parseInt('1', 0),
parseInt('2', 1),
parseInt('3', 2)
]
這時候,是不是就可以快速說出答案了:[1, NaN, NaN]
族扰。
借機徹底弄懂了 parseInt()
的方法厌丑,可以滿意地離開了,The end.