【JS】为什么['1', '7', '11'].map(parseInt) returns [1, NaN, 3]

论坛 期权论坛 期权     
程序员早   2019-6-16 23:22   2132   0
Javascript很奇怪。不相信我?尝试使用map和parseInt将字符串数组转换为整数数组。启动控制台(Chrome上的F12),粘贴以下内容,然后按Enter。
['1', '7', '11'].map(parseInt);
我们最终得到的不是
  1. [1, 7, 11]
复制代码
复制代码
而是
  1. [1, NaN, 3]
复制代码
。为什么?要了解到底发生了什么,我们首先要讨论一些Javascript概念。如果你想快速知道,本问尾处加入了一个快速摘要。
真与假
这是Javascript中一个简单的if-else语句:
if (true) {
// this always runs
} else {
// this never runs
}
在这种情况下,if-else语句的条件为true,因此始终执行if-block并忽略else-block。这是一个简单的例子,因为true是一个布尔值。如果我们将非布尔值作为条件怎么办?
if ("hello world") {
// will this run?
console.log("Condition is truthy");
} else {
// or this?
console.log("Condition is falsy");
}
尝试在开发者工具的控制台中运行此代码(Chrome里F12)。您应该发现if-block运行。这是因为字符串对象"hello world"是真实的。
每个Javascript对象都是真的或假的。当放置在布尔上下文中时,例如if-else语句,基于对象的真实性将对象视为true或false。那么哪些对象是真实的,哪些是假的呢?这是一个简单的规则:
所有的值都是真的,除了:false,0,""(空字符串),null,undefined和NaN。
令人困惑的是,这意味着字符串"false",字符串"0",空对象{}和空数组[]都是真的。您可以通过将对象传递给布尔函数(例如Boolean("0"))来仔细检查。
就我们的目的而言,记住那0是假的就足够了。
基数
0 1 2 3 4 5 6 7 8 910
当我们从0到9计数时,每个数字(0-9)都有不同的符号。但是,一旦我们达到10,我们需要两个不同的符号(1和0)来表示数字。这是因为我们的十进制计数系统的基数为10。
不同的计数系统具有不同的基数,因此,相同的数字可以指不同计数系统中的数字。
十进制
二进制
十六进制
基数=10
基数=2
基数=16
0
0
0
1
01
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,这对应于上表中的以2为基数。
函数参数
Javascript中的函数可以使用任意数量的参数调用,即使它们不等于声明的函数参数的数量。缺少的参数被视为未定义,并且忽略额外的参数(但存储在类似数组的参数对象中)。
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作为一个参数传递给map()……对吗?
[1, 2, 3, 4, 5].map(console.log);
一些非常奇怪的事情正在发生。每次console.log调用都记录索引和完整数组,而不是仅记录值。
[1, 2, 3, 4, 5].map(console.log);
// The above is equivalent to
[1, 2, 3, 4, 5].map(
(val, index, array) => console.log(val, index, array)
);
// and not equivalent to
[1, 2, 3, 4, 5].map(
val => console.log(val)
);
当一个函数传递到map(),对于每次迭代,有三个参数传递到次函数:currentValue,currentIndex,和完整的array。这就是每次迭代记录三项的原因。
我们现在拥有解决这个谜团所需的所有部分。
综上
ParseInt有两个参数:string和radix。如果提供的基数是假的,则为默认情况下的十进制。
parseInt('11');             => 11
parseInt('11', 2);          => 3
parseInt('11', 16);         => 17
parseInt('11', undefined);  => 11 (基数为假)
parseInt('11', 0);          => 11 (基数为假)
现在让我们一步一步地运行我们的示例。
['1', '7', '11'].map(parseInt);
=>[1, NaN, 3]
// 第一次迭代: val = '1'
// index = 0
// array = ['1', '7', '11']
parseInt('1', 0, ['1', '7', '11']);
=>1
由于0是假的,因此为十进制。 parseInt()只接受两个参数,因此['1', '7', '11']忽略第三个参数。字符串'1'表示十进制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
在二进制,符号“11”表示十进制3。同样忽略最后一个参数。
摘要
  1. ['1', '7', '11'].map(parseInt)不能按预期工作,因为在每次迭代中map传递三个参数parseInt()。
复制代码
  1. 第二个参数index作为radix参数传递给parseInt 。
复制代码
  1. 因此,使用不同的基数解析数组中的每个字符串。
复制代码
  1. '7'以1为基数被转换成NaN,'11'以2为基数被转换成3,'1'以默认基数10被转换,因为索引0是假。
复制代码
因此,以下代码将按预期工作:
['1', '7', '11'].map(numStr => parseInt(numStr));
↓长按识别二维码,关注公众号↓

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:10
帖子:2
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP