爲何 ['1', '7', '11'].map(parseInt) 的結果是 [1, NaN, 3]?

原文: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 之外皆爲真值)。

引用自:Mozilla Developer: Truthy(真值)對象

這裏必定要劃重點:在布爾上下文中,除 false0""(空字符串)、nullundefined 以及 NaN 外,其它全部值都爲真值索引

基數

0 1 2 3 4 5 6 7 8 9 10ip

當咱們從 0 數到 9,每個數字的表示符號都是不同的(0-9),可是當咱們數到 10 的時候,咱們就須要兩個不一樣的符號 10 來表示這個值,這是由於,咱們的數學計數系統是一個十進制的。原型鏈

基數,是一個進制系統下,能使用僅僅超過一個符號表示的數字的最小值,不一樣的計數進制有不一樣的基數,因此,同一個數字在不一樣的計數體系下,表示的真實數據可能並不同,咱們來看一下下面這張在十進制、二進制以及十六進制不一樣值在具體表示方法:

十進制(基數: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 參數,由於, radix10 並不保證永遠有效(這並非規範的一部分)。

那麼看看下面這些的輸出:

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);

    radix0,是一個假值,在咱們的控制檯中,通常將使用 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))arrayparseInt 捨棄以後,獲得的結果分別是:parseInt('1', 0)parseInt('7', 1) 以及 parseInt('11', 2),也就是上面看到的 [1, NaN, 3]

正確的寫法應該是:

['1', '7', '11'].map(numStr => parseInt(numStr, 10));
相關文章
相關標籤/搜索