JS 中為啥 「'1', '7', '11'」.map(parseInt) 返回 「1, NaN, 3」
譯者:前端小智 原文:https://medium.com/dailyjs/parseint-mystery-7c4368ef7b21
為了保證可讀性,本文采用音譯而非直譯。
Javascript 一直是神奇的語言。 不相信我? 嘗試使用 map和 parseInt將字符串數組轉換為整數。打開 Chrome 的控制檯(F12),粘貼以下內容,然後按回車,查看輸出結果:
['1', '7', '11'].map(parseInt);
我們得到的不是一個整數數組 [1,7,11],而是 [1,NAN,3],要了解究竟發生了什麼,我們首先要討論一些Javascript概念。
真值(truthy) & 虛值(falsy)
以下是 JS 中一個簡單的 if-else語句:
if
(
true
)
{
// this always runs
}
else
{
// this never runs
}
在上例中, if 條件為 true,因此總是執行 if塊,忽略 else塊。這是一個簡單的例子,因為 true是一個布爾值。如果我們把非布爾值作為條件呢 ?
if
(
"hello world"
)
{
console
.
log
(
"Condition is truthy"
);
}
else
{
console
.
log
(
"Condition is falsy"
);
}
打開控制檯並運行上述代碼,會打印 Conditionistruthy,說明條件 "hello world" 為真(true)值。
在 JavaScript 中,Truthy (真值)指的是在 布爾值 上下文中轉換後的值為真的值。所有值都是真值,除非它們被定義為 falsy (即除了 false, 0, "", null, undefined 和 NaN 外)。
falsy(虛值)是在 Boolean 上下文中已認定可轉換為‘假‘的值。
JS中的對象不是真值就是虛值。
令人困惑的是,這意味著字符串 “false”,字符串 “0”,空對象 {}和空數組 []都是真的。 使用使用 Boolean 方法來驗證,如 Boolean("0")。
出於我們的目的,接下來只要記住 0是假的就行了。
基數
在數學上,基數(cardinal number)是集合論中刻畫任意集合大小的一個概念。兩個能夠建立元素間一一對應的集合稱為互相對等集合。例如3個人的集合和3匹馬的集合可以建立一一對應,是兩個對等的集合。
0
1
2
3
4
5
6
7
8
9
10
當我們從 0數到 9時,每個數字(0-9)都有不同的符號,但是當我們數到 10時,我們需要兩個不同的符號( 1和 0)來表示這個數字。這是因為我們的十進制計數系統的基數是 10。
基數是最小的數字,只能由多個符號表示。 不同的計數系統具有不同的基數,因此,相同的數字在不同的計數系統中可以表示不同的數字。
十進制
二進制
十六進制
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時, parseInt返回 3,這對應於上表中的二進制列。
函數參數
JS 中函數調用,我們可以傳入任意的參數,即使它們不等於聲明時的函數參數的數量。缺少的參數被視為 undefined 的,並且會忽略額外的參數,但會保存在類似數組的 arguments對象中。
function
foo
(
x
,
y
)
{
console
.
log
(
x
);
console
.
log
(
y
);
}
foo
(
1
,
2
);
// 打印 1, 2
foo
(
1
);
// 打印 1, undefined
foo
(
1
,
2
,
3
);
// 打印 1, 2
map()
map是 Es6 中新出的一個數組方法,它是一個高階函數,通過傳入一個函數進行邏輯操作,並返回一個數組, 例如,以下代碼將數組中的每個元素乘以 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];
現在,將 console.log作為參數傳給 map,來打印數組的元素:
[
1
,
2
,
3
,
4
,
5
].
map
(
console
.
log
);
等價於
[
1
,
2
,
3
,
4
,
5
].
map
((
val
,
index
,
array
)
=>
console
.
log
(
val
,
index
,
array
));
所以 map 回調方法中會傳入三個參數,分別是 當前遍歷的項,當前索引,及遍歷的整個數組。
原因
ParseInt有兩個參數: string和 radix。 如果提供的基數是虛值,則默認情況下,基數設置為 10。
parseInt
(
'11'
);
=>
11
parseInt
(
'11'
,
2
);
=>
3
parseInt
(
'11'
,
16
);
=>
17
parseInt
(
'11'
,
undefined
);
=>
11
(
radix
is
falsy
)
parseInt
(
'11'
,
0
);
=>
11
(
radix
is
falsy
)
現在一步一步解析開頭的事例。
[
'1'
,
'7'
,
'11'
].
map
(
parseInt
);
=>
[
1
,
NaN
,
3
]
// 第一次迭代: val = '1', index = 0, array = ['1', '7', '11']
parseInt
(
'1'
,
0
,
[
'1'
,
'7'
,
'11'
]);
=>
1
因為 0是虛值,基數設置為默認值 10。 parseInt()只接受兩個參數,因此忽略了第三個參數 ['1'、'7'、'11']。以10為基數的字符串 “1”表示數字 1。
// 第二次迭代: val = '7', index = 1, array = ['1', '7', '11']
parseInt
(
'7'
,
1
,
[
'1'
,
'7'
,
'11'
]);
=>
NaN
在基數 1系統中,符號 “7”不存在。與第一次迭代一樣,忽略最後一個參數。因此, parseInt()返回 NaN。
// Third iteration: val = '11', index = 2, array = ['1', '7', '11']
parseInt
(
'11'
,
2
,
[
'1'
,
'7'
,
'11'
]);
=>
3
在基數 2(二進制)系統中,符號 “11”表示數字 3。
至此原因已經明瞭了。 有興趣可以試著寫下下列的打印結果:
[
'1'
,
'7'
,
'11'
].
map
(
numStr
=>
parseInt
(
numStr
));
我是小智,公眾號「大遷世界」作者,對前端技術保持學習愛好者。我會經常分享自己所學所看的乾貨,在進階的路上,共勉!