'
1 '
, '
7 '
, '
11 '
].map(parseInt) 在 Javascript 中返回 [1, NaN, 3]?简单结论:因为map传递3个参数,parseInt接收2个参数,额外的参数导致了出错。如果是parseFloat就没问题了,因为parseFloat只接受1个参数。
???
JavaScript 很奇怪。不相信我?尝试使用 map 和 parseInt 将字符串数组转换为整数。启动您的控制台(Chrome 上的 F12),粘贴以下内容,然后按 Enter(或运行下面的笔)。
['1', '7', '11'].map(parseInt);
我们没有给我们一个整数数组[1, 7, 11]
,而是以[1, NaN, 3]
. 什么?要了解到底发生了什么,我们首先必须讨论一些 Javascript 概念。如果你想要一个 TLDR,我在这个故事的结尾包含了一个简短的总结。
这是 Javascript 中的一个简单的 if-else 语句:
if (true) {
// 这总是运行
} else {
// 这永远不会运行
}
在这种情况下,if-else 语句的条件为真,所以总是执行 if-block 而忽略 else-block。这是一个简单的例子,因为 true 是一个布尔值。如果我们把一个非布尔值作为条件呢?
if ("hello world") {
// 这会运行吗?
console.log("条件为真");
} else {
// 还是这个?
console.log("条件为假");
}
尝试在开发人员的控制台中运行此代码(Chrome 上为 F12)。您应该会发现 if 块运行。这是因为字符串对象"hello world"
是真实的。
每个 Javascript 对象要么是真的,要么是假的。当放置在布尔上下文中时,例如 if-else 语句,对象会根据其真实性被视为真或假。那么哪些对象是真的,哪些是假的呢?这是一个简单的规则:
所有值都是真实的,除了: false
, 0
, ""
(空字符串), null
, undefined
, 和 NaN
.
令人困惑的是,这意味着字符串"false"
、字符串"0"
、空对象{}
和空数组[]
都是真值。您可以通过将对象传递给布尔函数(例如Boolean("0");
)来仔细检查这一点。
为了我们的目的,记住这0
是假的就足够了。
0 1 2 3 4 5 6 7 8 9 10
当我们从零数到九时,每个数字(0-9)都有不同的符号。但是,一旦达到十,我们就需要两个不同的符号(1 和 0)来表示数字。这是因为我们的十进制计数系统的基数(或基数)为 10。
基数是最小的数,只能用一个以上的符号表示。不同的计数系统有不同的基数,因此,相同的数字在计数系统中可以指代不同的数字。
十进制二进制十六进制
基数=10基数=2基数=160 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甲
11 1011乙
12 1100℃
13 1101 d
14 1110ë
15 1111˚F
16万10
17 10001 11
例如,查看上表,我们看到相同的数字 11 在不同的计数系统中可能表示不同的数字。如果基数是 2,那么它指的是数字 3。如果基数是 16,那么它指的是数字 17。
您可能已经注意到,在我们的示例中,当输入为 11 时,parseInt 返回 3,这对应于上表中的 Binary 列。
可以使用任意数量的参数调用 Javascript 中的函数,即使它们不等于声明的函数参数的数量。缺少的参数被视为未定义,额外的参数将被忽略(但存储在类似数组的参数对象中)。
函数 foo(x, y) {
console.log(x);
控制台日志(y);
}foo(1, 2); // 记录 1, 2
foo(1); // 记录 1, 未定义
foo(1, 2, 3); // 记录 1, 2
我们快到了!
Map 是 Array 原型中的一个方法,它返回将原始数组的每个元素传递给函数的结果的新数组。例如,以下代码将数组中的每个元素乘以 3:
函数乘以3(x){
返回x * 3;
}const 结果 = [1, 2, 3, 4, 5].map(multiplyBy3);控制台日志(结果);// 记录 [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);// 上面等价于[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 有两个参数:string
和radix
。如果提供的基数为假,则默认情况下基数设置为 10。
parseInt('11'); => 11
parseInt('11', 2); => 3
parseInt('11', 16); => 17parseInt('11', 未定义); => 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 为假,因此基数设置为默认值 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
传递了三个参数parseInt()
。第二个参数index
作为radix
参数传递给 parseInt 。因此,数组中的每个字符串都使用不同的基数进行解析。'7'
解析为基数 1,即NaN
,'11'
解析为基数 2,即 3。'1'
解析为默认基数 10,因为其索引 0 为假。
因此,以下代码将按预期工作:
['1', '7', '11'].map(numStr => parseInt(numStr));
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。