原文:https://pantao.parcmg.com/press/parseint-mystery.htmlhtml
Javascript 老是以超天然的方式執行咱們的代碼,這是一件很神奇的事情,若是不信的話,思考一下 ['1', '7', '11'].map(parseInt)
的結果是什麼?你覺得會是 [1, 7, 11]
嗎?我都這麼問了,那確定不是:數組
['1', '7', '11'].map(parseInt) // 輸出:(3) [1, NaN, 3]
要理解爲何爲會這裏,首先須要瞭解一些 Javascript 概念,若是你是一個不太喜歡閱讀長文的人,那直接跳到最後看結論吧函數
請看下面示例:工具
if (true) { // 這裏將啓動都會被執行 } else { // 這裏永遠都不會被執行 }
在上面的示例中, if-else
聲明爲 true
,因此 if-block
會一直都執行,而 else-block
永遠都會被忽略掉,這是個很容易理解的示例,下面咱們來看看另外一個例子:code
if ("hello world") { // 這裏會被執行嗎? console.log("條件判斷爲真"); } else { // 或者會執行這裏嗎? console.log("條件判斷爲假"); }
打開你的開發者工具 console
面板,執行上面的代碼,你會獲得看到會打印出 條件判斷爲真,也就是說 "hello world"
這一個字符串被認爲是真值。htm
在 JavaScript 中,truthy(真值)指的是在布爾值上下文中,轉換後的值爲真的值。全部值都是真值,除非它們被定義爲 假值(即除 false、0、""、null、undefined 和 NaN 之外皆爲真值)。
這裏必定要劃重點:在布爾上下文中,除 false
、0
、""
(空字符串)、null
、undefined
以及 NaN
外,其它全部值都爲真值索引
0 1 2 3 4 5 6 7 8 9 10ip
當咱們從 0
數到 9
,每個數字的表示符號都是不同的(0-9),可是當咱們數到 10
的時候,咱們就須要兩個不一樣的符號 1
與 0
來表示這個值,這是由於,咱們的數學計數系統是一個十進制的。原型鏈
基數,是一個進制系統下,能使用僅僅超過一個符號表示的數字的最小值,不一樣的計數進制有不一樣的基數,因此,同一個數字在不一樣的計數體系下,表示的真實數據可能並不同,咱們來看一下下面這張在十進制、二進制以及十六進制不一樣值在具體表示方法:
十進制(基數:10) | 二進制(基數:2) | 十六進制(基數:16) |
---|---|---|
0 | 0 | 0 |
1 | 1 | 1 |
2 | 10 | 2 |
3 | 11 | 3 |
4 | 100 | 4 |
5 | 101 | 5 |
6 | 110 | 6 |
7 | 111 | 7 |
8 | 1000 | 8 |
9 | 1001 | 9 |
10 | 1010 | A |
11 | 1011 | B |
12 | 1100 | C |
13 | 1101 | D |
14 | 1110 | E |
15 | 1111 | F |
16 | 10000 | 10 |
17 | 10001 | 11 |
18 | 10002 | 12 |
你應該已經注意到了, 11 在上表中,總共出現了三次。
在 Javascript 中,一個函數能夠傳遞任何多個數量的參數,即便調用時傳遞的數量與定義時的數量不一致。缺失的參數會以 undefined
做爲實際值傳遞給函數體,而後多餘的參數會直接被忽略掉(可是你仍是能夠在函數體內經過一個類數組對象 arguments
訪問到)。
function sum(a, b) { console.log(a); console.log(b); } sum(1, 2); // 輸出:1, 2 sum(1); // 輸出:1, undefined sum(1, 2, 3); // 輸出:1, 2
map()
快要有結果了。
map()
是一個存在於數組原型鏈上的方法,它將其數組實例中的每個元素,傳遞給它的第一個參數(在本文最開始的例子中就是 parseInt
),而後將每個返回值都保存到同一個新的數組中,遍歷完全部元素以後,將新的包含了全部結果的數組返回。
function multiplyBy3 (number) { return number * 3; } const result = [1, 2, 3, 4, 5].map(multiplyBy3); console.log(result); // 輸出:[3, 6, 9, 12, 15]
如今,假想一下,咱們想使用 map()
將一個數組中的每個元素都打印到控制檯,我能夠直接將 console.log
函數傳遞給 map()
方法:
[1, 2, 3, 4, 5].map(console.log);
輸出:
1 0 (5) [1, 2, 3, 4, 5] 2 1 (5) [1, 2, 3, 4, 5] 3 2 (5) [1, 2, 3, 4, 5] 4 3 (5) [1, 2, 3, 4, 5] 5 4 (5) [1, 2, 3, 4, 5]
是否是感受有點神奇了?爲何會這樣?來分析一下上面輸出的內容都有些什麼?
0
開始遞增的數字(5) [1, 2, 3, 4, 5]
再來看看下面這段代碼:
[1, 2, 3, 4, 5].map((value, index, array) => console.log(value, index, array));
在控制檯上試試執行上面這行代碼,你會發現,它與 [1, 2, 3, 4, 5].map(console.log);
輸出的結果是徹底一致的,**老是會被咱們忽略的一點是,map()
方法會將三個參數傳遞傳遞給它的函數,分別是 currentValue
(當前值)、currentIndex
(當前索引)以及 array
自己,這就是,上面爲何結果有三列的緣由,若是咱們只是想將每個值打印,那麼須要像下面這樣寫:
[1, 2, 3, 4, 5].map((value) => console.log(value));
如今讓咱們來回顧一下本文最開始的問題:
爲何 ['1', '7', '11'].map(parseInt)
的結果是 [1, NaN, 3]
?
來看看 parseInt()
是一個什麼樣的函數:
parseInt(string, radix)
將一個字符串string
轉換爲radix
進制的整數,radix
爲介於2-36
之間的數。詳情:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/parseInt
這裏還有一條須要注意的是,雖然咱們通常都是使用 parseInt('11')
這樣的調用方式,認爲是將 11
按十進制轉換成數字,可是,請在使用的時候,永遠都添加 radix
參數,由於, radix
爲 10
並不保證永遠有效(這並非規範的一部分)。
那麼看看下面這些的輸出:
parseInt('11'); // 輸出:11 parseInt('11', 2); // 輸出:3 parseInt('11', 16); // 輸出:17 parseInt('11', undefined); // 輸出:11 (radix 是假值) parseInt('11', 0); // 輸出:11 (radix 是假值)
如今,讓咱們一步一步來看 ['1', '7', '11'].map(parseInt)
的整個執行過程,首先,咱們能夠將這一行代碼,轉換爲完整的版本:
['1', '7', '11'].map((value, index, array) => parseInt(value, index, array))
根據前面的知識,咱們應該知道, parseInt(value, index, array)
中的 array
會被忽略(並放入 arguments
對象中。
那麼,完整的代碼就是下面這樣的:
['1', '7', '11'].map((value, index, array) => parseInt(value, index))
遍歷到第一個元素時:
parseInt('1', 0);
radix
爲 0
,是一個假值,在咱們的控制檯中,通常將使用 10
進制,全部,獲得結果 1
遍歷到第二個元素時:
parseInt('7', 1)
在一個 1
進制系統中,7
是不存在的,因此獲得結果 NaN
(不是一個數字)
遍歷到第三個元素時:
parseInt('11', 2)
在一個 2
進制系統中,11
就是進十制的 3
['1', '7', '11'].map(parseInt)
的結果是 [1, NaN, 3]
的緣由是由於,map()
方法是向傳遞給他的函數中傳遞三個參數,分別爲當前值,當前索引以及整個數組,而 parseInt
函數接收兩個參數:須要轉換的字符串,以及進制基數,因此,整個語句能夠寫做:['1', '7', '11'].map((value, index, array) => parseInt(value, index, array))
,array
被 parseInt
捨棄以後,獲得的結果分別是:parseInt('1', 0)
、parseInt('7', 1)
以及 parseInt('11', 2)
,也就是上面看到的 [1, NaN, 3]
。
正確的寫法應該是:
['1', '7', '11'].map(numStr => parseInt(numStr, 10));