为什么 ['1', '7', '11'].map(parseInt) 返回 [1, NaN, 3]

翻译 alentan

原文 medium.com/dailyjs

批注 2019-07-12 104044.png

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 语句的条件为 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
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,125评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,293评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,054评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,077评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,096评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,062评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,988评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,817评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,266评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,486评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,646评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,375评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,974评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,621评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,642评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,538评论 2 352

推荐阅读更多精彩内容