翻译 alentan
批注 2019-07-12 104044.pngJavascript很奇怪。不相信我?尝试使用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 语句的条件为 true,因此始终执行 if-block 语句块并忽略 else-block 语句块。这是一个简单的例子,因为 true 是一个布尔值。如果我们将非布尔值作为条件会怎么样呢?
if ("hello world") {
// 他会运行吗?
console.log("Condition is truthy");
} else {
// 还是运行这个?
console.log("Condition is falsy");
}
复制代码
尝试在浏览器的控制台中运行此代码(Chrome上的F12)。您应该发现 if-block 语句块运行。这是因为字符串对象"hello world"是true。
每个Javascript对象当放置在布尔上下文中时都是真值或假值。例如 if-else 语句,会把Javascript对象转为true或false。那么哪些对象是true的,哪些是false的呢?这是一个简单的规则:
Javascript中当以下的值放置在布尔上下文中时会返回false
false,0(""空字符串),null,undefined和NaN。
进制(基数)
0 1 2 3 4 5 6 7 8 9 10
复制代码
当我们从0到9计数时,每个数字(0-9)都有不同的符号。但是,一旦我们达到10,我们需要两个不同的符号(1和0)来表示数字。这是因为我们的小数计数系统的基数(或进制)为10。
基数是最小的数字,只能由多个符号表示。不同的计数系统具有不同的基数,因此,相同的数字可以指计数系统中的不同数字。
10进制 二进制 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,这对应于上表中的二进制列。
函数参数
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);
复制代码
[图片上传失败...(image-4ee30b-1562899087851)]
<figcaption></figcaption>
一些非常奇怪的事情正在发生。每次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(进制)。如果提供的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是假的,因此进制(基数)设置为默认值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,最后一个参数被忽略。
摘要(TLDR)
['1', '7', '11'].map(parseInt) 不能按预期工作,因为在每次迭代中 map 传递三个参数到 parseInt()。第二个参数 index 作为radix(进制)参数传递给 parseInt 。因此,使用不同的进制(基数)解析数组中的每个字符串。'7' 被解析为进制(基数)为 1,它是 NaN,'11' 被解析为进制(基数)为 2,它的值为 3,'1' 被解析为默认的进制(基数)为 10,因为它的索引 0 是假值。
作者:alentan
链接:https://juejin.im/post/5d0202da51882546dd10087b
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
网友评论