天天看點

JavaScript | 為何 ['1','7','11'].map(parseInt) = [1, NaN, 3] 【譯】

javascript 是古怪的。不信??試着用

map

parseInt

将一個字元串數組轉成數字數組。将下面的段代碼輸入到控制台(在Chrome浏覽器按 F12),并回車。

['1', '7', '11'].map(parseInt);
           

複制

我們得到結果是

[1, NaN, 3]

,而不是我們想要的

[1, 7, 11]

。為何?為了探究其原因,我們先探讨一些javascript觀點。如果你是個’太長不看’,可以直接到總結部分。

JavaScript | 為何 ['1','7','11'].map(parseInt) = [1, NaN, 3] 【譯】

true 或者 false

看下面這個

if-else

的簡單條件語句。

if (true) {
    // 總是執行
} else {
    // 絕對不會執行
}
           

複制

在這個例子中,

if-else

的語句總是

true

,是以

if

下的塊語句總是執行,而

else

語句塊絕不會執行。這很好了解,因為

true

boolean

。如果不是

boolean

作為條件的話會是怎麼樣呢?

if ("hello world") {
    // 執行這個?
    console.log("Condition is true");
} else {
    // 還是這個?
    console.log("Condition is false");
}
           

複制

當你在控制台裡執行後,會發現

if

下的語句塊被執行,說明

string

對象

"hello world"

轉成

boolean

值為

true

。所有的javascript的值要麼是

true

要麼是

false

。當在一個

boolean

環境下時,如

if-else

,都會被轉成

true

false

。那麼這個轉換規則是什麼呢?可以參考以下這個規則:

⚠️ 除了

false

, ,

""

(空字元串),

null

,

undefined

, 和

NaN

,其餘的值都是轉成

true

也就是說,字元串

"false"

,

"0"

,以及空對象

{}

,空數組

[]

都是被轉成

true

。你可以用

Boolean

方法驗證下,例如

Boolean("0")

。在我們這個文章中,我們隻用到

轉成

false

基數

0 1 2 3 4 5 6 7 8 9 10
           

複制

當我們從一數到九,我們有不同的字元表示每個數字(0-9)。然而,當數到十的時候,我們用兩個字元表示(1和0)。這是因為我們采用的十進制,以10為基數(radix or base)。基數就是用一個字元表示的最小機關的數量。不同的計數方法有不同的基數,在不同的基數下一些相同的字元表示不同的數字。

十進制   二進制    十六進制
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,那麼11表示的是數字3,如果基數是16,那麼表示數字17。你可能注意到了,我們的例子中的11被

parseInt

後變成了3,也就是基數為2的時候。

Function arguments

Javascript中的function,不管聲明時是幾個變量,調用時可以傳任意個參數。沒有傳的預設為

undefined

,超出的部分被忽略(但會儲存在

arguments

對象中)。

function foo(x, y) {
    console.log(x, y);
}
foo(1, 2);      // 1 2
foo(1);         // 1 undefined
foo(1, 2, 3);   // 1 2
           

複制

map()

map

是數組

Array

的一個方法,傳回的是一個數組,這個數組的值是由原數組的每一個值通過一個新方法映射的。例如,下面的代碼就是把數組裡所有的數乘以3。

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

複制

現在我們可以用利用

map()

傳入

console.log

列印所有元素。

[1, 2, 3, 4, 5].map(console.log);           

複制

JavaScript | 為何 ['1','7','11'].map(parseInt) = [1, NaN, 3] 【譯】

哇?!發現列印的不僅僅是數值,還列印了index和整個數組。

[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()時,對于每一個疊代器,都傳了3個參數給這個函數:

currentValue

(目前值),

currentIndex

(目前索引), 和

array

(原數組)。這就是為什麼會列印三個值了。好的?,我們現在已經揭開部分謎底了。

綜合起來看

parseInt

需要兩個參數:字元串和基數。當基數作為條件的時候是

false

時,基數預設為10。

parseInt('11');               // 11
parseInt('11', 2);            // 3
parseInt('11', 16);           // 17
parseInt('11', undefined);    // 11 (基數 是 false)
parseInt('11', 0);            // 11 (基數 是 false)
           

複制

現在,讓我們一步一步分解我們的例子:

['1', '7', '11'].map(parseInt);     // [1, NaN, 3]
// 第一次疊代: val = '1', index = 0, array = ['1', '7', '11']
parseInt('1', 0, ['1', '7', '11']); // 1
           

複制

因為

Boolean(0)

false

,是以預設的基數為10。

parseInt

隻接收兩個參數,是以第三個參數

['1', '7', '11']

被忽略了。字元串

'1'

在基數10中為數字1。

// 第二次疊代: 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

在每次疊代傳了3個參數給

parseInt

。第二個參數

index

(目前索引)被

parseInt

作為基數參數了。是以,每個字元串被解析成不同的基數。

'7'

用基數1,傳回

NaN

,

'11'

用基數2,傳回

3

。因為

Boolean(0)

false

'1'

用預設基數10,傳回

1

是以,我們想要的結果應該是如下代碼:

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

複制

JavaScript | 為何 ['1','7','11'].map(parseInt) = [1, NaN, 3] 【譯】