首先看下 parseInt
的函數簽名
/**
* Converts a string to an integer.
* @param s 要被解析的值。如果參數不是一個字符串,則將其轉換爲字符串(使用 ToString 抽象操作)。字符串開頭的空白符將會被忽略。
* @param radix 一個介於2和36之間的整數(數學系統的基礎),表示上述字符串的基數。
* 如果不提供這個參數,以 `0x` 開頭的字符串會被識別成16進制數.
* 其它情況,不同環境有不同的處理方式,一般會被識別成10進制數
* 如果傳入 0 null undefined 等,當不提供這個參數處理
* 如果傳入不是數字的會把數字,會把參數轉成數字,如果沒法轉化,當不提供這個參數處理
* 不提供這個參數不同平臺可能會有不同,所以,請顯式提供這個參數
*/
declare function parseInt(s: string, radix?: number): number;
可以看到 parseInt
其實有兩個參數,一個是必填的 s
,類型是 string
;
還有一個可選的 radix
,用來表示進制,例如填寫 10
的話,會把第一個參數解析成十進制,如果填寫 16
,會把傳進來的字符串解析成十六機制,原則上,這個參數最好都填寫。
返回值是一個整數。如果被解析參數的第一個字符無法被轉化成數值類型,則返回 NaN
。
幾個簡單的例子
以下這堆都返回 15
parseInt('0xF', 16);
parseInt('F', 16);
parseInt('17', 8);
parseInt(021, 8);
parseInt('015', 10); // parseInt(015, 10); 返回 15
parseInt(15.99, 10);
parseInt('15,123', 10);
parseInt('FXX123', 16);
parseInt('1111', 2);
parseInt('15 * 3', 10);
parseInt('15e2', 10);
parseInt('15px', 10);
parseInt('12', 13);
以下例子均返回 -15
parseInt('-F', 16);
parseInt('-0F', 16);
parseInt('-0XF', 16);
parseInt(-15.1, 10);
parseInt(' -17', 8);
parseInt(' -15', 10);
parseInt('-1111', 2);
parseInt('-15e1', 10);
parseInt('-12', 13);
以下例子都返回 NaN
parseInt('Hello', 8); // 根本就不是數值
parseInt('546', 2); // 除了“0、1”外,其它數字都不是有效二進制數字
parseInt('zz', 35); // z 是 35,在 35 進制裏面不可能有 35
返回 NaN
的例子改一下
parseInt('Hello', 18); // 320, H = 17 e = 14 l = 21,可以解析的 He 17 * 18 ^ 1 + 14 * 18 ^ 0 = 320
// 188275, H = 17 e = 14 l = 21,可以解析的 Hell 17 * 22 ^ 3 + 14 * 22 ^ 2 + 21 * 22 ^ 1 + 21 * 22 ^ 0 = 188275
parseInt('Hello', 22);
parseInt('546', 10); // 546 這個不說了
parseInt('zz', 36); // 1295 z = 35,35 * 36 ^ 1 + 35 * 36 ^ 0 = 1295
傳入數字,以下例子都返回 4
,先 toString()
,然後小數點是沒法解析的
parseInt(4.7, 10);
parseInt(4.7 * 1e22, 10); // 非常大的數值變成 4
parseInt(0.00000000000434, 10); // 非常小的數值變成 4
最近的一些面試題和變種
[1,2,3].map(parseInt)
// [1, NaN, NaN]
map<U>(callbackfn: (value: T, index: number, array: ReadonlyArray<T>) => U, thisArg?: any): U[];
想必很多人都知道答案了,演算過程說一下大概就是,這裏還需要了解一些 map 的函數簽名,map 第一個參數是回調函數,有三個形參,雖然平時一般不會用到第三個,parseInt 接受的參數有兩個,所以實際運行起來的是這樣的
[1, 2, 3].map((value, index) => parseInt(value, index));
三次演算過程分別是
-
parseInt(1, 0)
,按十進制處理,返回1
-
parseInt(2, 1)
,基數不可能是1
,返回NaN
-
parseInt(3, 2)
,二進制裏面不可能出現3
的,返回NaN
[1, 2, 3].filter(parseInt); //[1]
[1, 2, 3].find(parseInt); // 1
[1, 2, 3].findIndex(parseInt); // 0
filter
的回調函數簽名和 map
是一樣的,filter
的作用是,過濾掉返回值是假值的函數,find
是找到第一個返回值是真值的 item
,findIndex
的作用是,找到第一個返回是真值的 index
演算過程
-
parseInt(1, 0)
,按十進制處理,返回1
,真值,find
返回1
,findIndex
返回0
, -
parseInt(2, 1)
,基數不可能是1
,返回NaN
,假值,被過濾了 -
parseInt(3, 2)
,二進制裏面不可能出現3
的,返回NaN
,假值,被過濾了
這裏提一下, find
和 findIndex
是可以用來做短路運算的,碰到 true
,就會退出,可以用來替代 forEach
這種沒法通過 return
退出的,但是這樣代碼語義化就很弱了。
[1,2,3].reduce(parseInt)
// 1
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: ReadonlyArray<T>) => T, initialValue: T): T;
reduce
的回調參數有四個,一個是上一次循環返回的值,一個是當前值,一個是當前值索引,一個是原數組。這道題裏面沒有涉及到初始值,所以迭代是從 2
開始的。
兩次演算過程分別是:
-
parseInt(1, 2)
, 二進制1
, 轉成十進制也是1
,這時候,返回1
,進入下一輪迭代 -
parseInt(1, 3)
, 第二次迭代,上一次迭代的值是1
,當前值是3
, 三進制的1
,返回1
[1, 2, 3].reduceRight(parseInt);
// NaN
reduceRight
的簽名和 reduce
一樣,不過迭代是從右邊開始的。
兩次演算過程分別是:
-
parseInt(3, 2)
, 二進制3
, 返回NaN
-
parseInt(NaN, 1)
, 第二次迭代,上一次迭代的值是NaN
,當前值是1
, 基數爲1
,返回NaN
[1, 2, 3].some(parseInt); // true
[1, 2, 3].every(parseInt); // false
some
every
的回調函數簽名和 map
一樣,some
是有真值就返回 true, every
是全部都是真值才返回 true
。
演算過程分別是
-
parseInt(1, 0)
,按十進制處理,返回1
,真值,some
返回true
,運算結束 -
parseInt(2, 1)
,基數不可能是1
,返回NaN
,假值,every
返回false
,運算結束
這裏提一下, some
和 every
是可以用來做短路運算的,some
碰到 true
,就會退出, every
碰到 false
就會退出,可以用來替代 forEach
這種沒法通過 return
退出的,但是這樣代碼語義化就很弱了。
[1, 2, 3].forEach(parseInt); // true
// undefined
這道題要是答錯了,是真的要給自己一巴掌。。。。
[1, 2, 3].map(parseInt);
有沒有經過類型轉換?有的。因爲 parseInt
會把第一個傳進來的參數,如果不是 string
就要轉成 string
,這裏有個類型轉換
寫在最後
一個看似很簡單的 API
,其實考法還挺多的,如果不仔細瞭解的話,就不會知道其中的原理。