[譯]爲何 ['1', '7', '11'].map(parseInt) 在 Javascript 中返回了 [1, NaN, 3]

本篇文章採用意譯,原文地址數組

你有沒有以爲 Javascript 是有點奇怪的。使用 mapparseInt 試着把字符串數組轉化成整型數組,看看會發生什麼。打開你的控制檯,粘貼下面的代碼而後執行。bash

['1', '7', '11'].map(parseInt);
複製代碼

沒有獲得 [1,7,11,卻獲得了 [1, NaN, 3]。這到底是怎麼回事?咱們首先須要討論一些 Javascript 的概念,若是你以爲太長,能夠跳到最後看總結。函數

真值和假值

一個簡單的 if-else 語句:ui

if (true) {
    // this always runs
} else {
    // this never runs
}
複製代碼

在這個例子中,條件爲真,執行 if 的語句塊,條件爲假執行 else 的語句塊。這是顯而易見的,由於 true 是個布爾值。咱們把非布爾值的東西做爲條件,會發生什麼?this

if ("hello world") {
    // 它會運行嗎?
    console.log("Condition is truthy");
} else {
    // 它呢?
    console.log("Condition is falsy");
}
複製代碼

在控制檯裏運行上面的代碼,你會發現 if 語句塊執行了。由於這個字符串對象是真值。spa

Javascript 對象要麼是真值要麼是假值。當在布爾值上下文中,好比 if-else 語句,對象被當作真或者假取決於它們的真實性。這個規則很簡單:日誌

除了:false, 0, ""(空字符串),null, undefined, 和 NaN 以外,其餘的都是真值。code

使人困惑的是,字符串 "false" 和 字符串 "0" 還有空對象 {}, 空數組 [],都是真值。你能夠經過給布爾函數傳值來進行雙向驗證,好比 Boolean("0")cdn

咱們的目的是,知道 0 是假值就足夠了。對象

基數

0 1 2 3 4 5 6 7 8 9 10
複製代碼

當咱們從 0 數到 9 的時候,每一個不一樣的符號表明一個數字。然而到 10 的時候,咱們須要兩個符號來表示。這是由於咱們是十進制系統,它的基數是 10.

基數是用一個以上的符號表示的最小數字。不一樣的計數系統有着不一樣的基數。

DECIMAL   BINARY    HEXADECIMAL
RADIX=10  RADIX=2   RADIX=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
複製代碼

看看上面的表,數字 11 在不一樣的系統中表示也不一樣。若是基數是 2,它是 3,若是基數是 16,它是 17.

咱們上面的例子中吧 '11' 轉換成了 3,就是上面表格二進制的表現。

函數參數

Javascript 中的函數能夠傳遞任意數量的參數,即便跟函數聲明的參數不相等。缺乏的參數會被當作 undefined,多餘的參數會被忽略(可是會存儲在類數組的 arguments 對象中)。

function foo(x, y) {
    console.log(x);
    console.log(y);
}
foo(1, 2);      // logs 1, 2
foo(1);         // logs 1, undefined
foo(1, 2, 3);   // logs 1, 2
複製代碼

map()

立刻就到咱們的重點了!

Map 是 Array 原型上的方法,它返回一個每一個原始數組的元素傳入函數的結果的新數組。好比下面代碼,每一個元素在數組中乘 3:

function multiplyBy3(x) {
    return x * 3;
}
const result = [1, 2, 3, 4, 5].map(multiplyBy3);
console.log(result);   // logs [3, 6, 9, 12, 15];
複製代碼

那麼如今若是我須要日誌輸出每一個元素,是否是使用 map() 而後傳入 console.log 就能夠了呢?

[1, 2, 3, 4, 5].map(console.log);
複製代碼

奇怪的事,不僅是輸出了值,同時把索引和所有數組也輸出了。

[1, 2, 3, 4, 5].map(console.log);
// 上面的代碼等於
[1, 2, 3, 4, 5].map(
    (val, index, array) => console.log(val, index, array)
);
// 不等於
[1, 2, 3, 4, 5].map(
    val => console.log(val)
);
複製代碼

當給 map() 傳入一個方法時,對於每次迭代,都會有三個參數傳入方法:currentValue, currentIndex, 和所有 array。這就是爲何每次迭代都會輸出所有三個實體內容。

如今離解開咱們的謎題愈來愈近了。

揭曉答案

ParseInt 接受兩個參數:stringradix。若是 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, NaN, 3]
// 第一次遍歷: val = '1', index = 0, array = ['1', '7', '11']
parseInt('1', 0, ['1', '7', '11']);   => 1
複製代碼

由於 0 是假值,因此 radix 基數取值爲 10。 由於 parseInt 只接受兩個參數,因此第三個 ['1', '7', '11'] 參數被忽略了。

// 第二次遍歷: val = '7', index = 1, array = ['1', '7', '11']
parseInt('7', 1, ['1', '7', '11']);   => NaN
複製代碼

由於在基數爲 1 的系統中, 7 不存在。同時第三個參數仍然跟第一次迭代同樣被省略。因此 parseInt() 返回了 NaN

// 第三次迭代: val = '11', index = 2, array = ['1', '7', '11']
parseInt('11', 2, ['1', '7', '11']);   => 3
複製代碼

基數爲 2 的系統中, 符號 11 得出數字 3。最後的參數被省略。

總結

['1', '7', '11'].map(parseInt) 沒有像預期那樣工做,是由於 map 給 parseInt 傳遞了三個參數。第二個參數 index 做爲 radix 參數傳入了 parseInt。因此每一個數組的字符串使用了不一樣的基數來解析。'7' 被解析爲基數爲 1 的結果,也就是 NaN,11 被解析爲基數爲 2 的結果,也就是 3。1 被做爲默認值解析,由於索引 0 是假值。因此,下面的代碼才能正常工做:

['1', '7', '11'].map(numStr => parseInt(numStr));
複製代碼

pic
相關文章
相關標籤/搜索