JS 中为啥 ['1', '7', '11'].map(parseInt) 返回 [1, NaN, 3]

论坛 期权论坛 期权     
大迁世界   2019-6-29 21:28   3283   0
译者:前端小智
原文:https://medium.com/dailyjs/parseint-mystery-7c4368ef7b21
为了保证可读性,本文采用音译而非直译。


Javascript 一直是神奇的语言。 不相信我? 尝试使用
  1. map
复制代码
  1. parseInt
复制代码
将字符串数组转换为整数。打开 Chrome 的控制台(F12),粘贴以下内容,然后按回车,查看输出结果:
['1', '7', '11'].map(parseInt);
我们得到的不是一个整数数组
  1. [1,7,11]
复制代码
,而是
  1. [1,NAN,3]
复制代码
,要了解究竟发生了什么,我们首先要讨论一些Javascript概念。

[h1]真值(truthy) & 虚值(falsy)[/h1]以下是 JS 中一个简单的
  1. if-else
复制代码
语句:
    1. if (true) {
    复制代码
    1.     // this always runs
    复制代码
    1. } else {
    复制代码
    1.     // this never runs
    复制代码
    1. }
    复制代码
在上例中,
  1. if
复制代码
条件为
  1. true
复制代码
,因此总是执行
  1. if
复制代码
块,忽略
  1. else
复制代码
块。这是一个简单的例子,因为
  1. true
复制代码
是一个布尔值。如果我们把非布尔值作为条件呢 ?
    1. if ("hello world") {
    复制代码
    1.     console.log("Condition is truthy");
    复制代码
    1. } else {
    复制代码
    1.     console.log("Condition is falsy");
    复制代码
    1. }  
    复制代码
打开控制台并运行上述代码,会打印
  1. Conditionistruthy
复制代码
,说明条件
  1. "hello world"
复制代码
为真(true)值。
在 JavaScript 中,Truthy (真值)指的是在 布尔值 上下文中转换后的值为真的值。所有值都是真值,除非它们被定义为
  1. falsy
复制代码
(即除了
  1. false
复制代码
  1. 0
复制代码
  1. ""
复制代码
  1. null
复制代码
  1. undefined
复制代码
  1. NaN
复制代码
外)。
falsy(虚值)是在 Boolean 上下文中已认定可转换为‘假‘的值。
JS中的对象不是真值就是虚值。
令人困惑的是,这意味着字符串
  1. “false”
复制代码
,字符串
  1. “0”
复制代码
,空对象
  1. {}
复制代码
和空数组
  1. []
复制代码
都是真的。 使用使用 Boolean 方法来验证,如
  1. Boolean("0")
复制代码

出于我们的目的,接下来只要记住
  1. 0
复制代码
是假的就行了。

[h1]基数[/h1]在数学上,基数(cardinal number)是集合论中刻画任意集合大小的一个概念。两个能够建立元素间一一对应的集合称为互相对等集合。例如3个人的集合和3匹马的集合可以建立一一对应,是两个对等的集合。
    1. 0 1 2 3 4 5 6 7 8 9 10
    复制代码
当我们从
  1. 0
复制代码
数到
  1. 9
复制代码
时,每个数字(0-9)都有不同的符号,但是当我们数到
  1. 10
复制代码
时,我们需要两个不同的符号(
  1. 1
复制代码
  1. 0
复制代码
)来表示这个数字。这是因为我们的十进制计数系统的基数是
  1. 10
复制代码

基数是最小的数字,只能由多个符号表示。 不同的计数系统具有不同的基数,因此,相同的数字在不同的计数系统中可以表示不同的数字。
    1. 十进制   二进制    十六进制
    复制代码
    1. RADIX=10  RADIX=2   RADIX=16
    复制代码
    1. 0         0         0
    复制代码
    1. 1         1         1
    复制代码
    1. 2         10        2
    复制代码
    1. 3         11        3
    复制代码
    1. 4         100       4
    复制代码
    1. 5         101       5
    复制代码
    1. 6         110       6
    复制代码
    1. 7         111       7
    复制代码
    1. 8         1000      8
    复制代码
    1. 9         1001      9
    复制代码
    1. 10        1010      A
    复制代码
    1. 11        1011      B
    复制代码
    1. 12        1100      C
    复制代码
    1. 13        1101      D
    复制代码
    1. 14        1110      E
    复制代码
    1. 15        1111      F
    复制代码
    1. 16        10000     10
    复制代码
    1. 17        10001     11
    复制代码
看上表,可以看到相同的数字
  1. 11
复制代码
在不同的计数系统中可以表示不同的数字。如果基数是
  1. 2
复制代码
,那么它表示数字为
  1. 3
复制代码
。如果基数是
  1. 16
复制代码
,那么它指的是数字
  1. 17
复制代码

你可能已经注意到,在我们的示例中,当输入为
  1. 11
复制代码
时,
  1. parseInt
复制代码
返回
  1. 3
复制代码
,这对应于上表中的二进制列。

[h1]函数参数[/h1]JS 中函数调用,我们可以传入任意的参数,即使它们不等于声明时的函数参数的数量。缺少的参数被视为
  1. undefined
复制代码
的,并且会忽略额外的参数,但会保存在类似数组的
  1. arguments
复制代码
对象中。
    1. function foo(x, y) {
    复制代码
    1.     console.log(x);
    复制代码
    1.     console.log(y);
    复制代码
    1. }
    复制代码
    1. foo(1, 2);      // 打印 1, 2
    复制代码
    1. foo(1);         // 打印 1, undefined
    复制代码
    1. foo(1, 2, 3);   // 打印 1, 2   
    复制代码
[h1]map()[/h1]
  1. map
复制代码
是 Es6 中新出的一个数组方法,它是一个高阶函数,通过传入一个函数进行逻辑操作,并返回一个数组, 例如,以下代码将数组中的每个元素乘以
  1. 3
复制代码

    1. function multiplyBy3(x) {
    复制代码
    1.     return x * 3;
    复制代码
    1. }
    复制代码
    1. const result = [1, 2, 3, 4, 5].map(multiplyBy3);
    复制代码
    1. console.log(result);   // logs [3, 6, 9, 12, 15];
    复制代码
现在,将
  1. console.log
复制代码
作为参数传给
  1. map
复制代码
,来打印数组的元素:
    1. [1, 2, 3, 4, 5].map(console.log);
    复制代码

    1. 等价于
    复制代码

    1. [1, 2, 3, 4, 5].map((val, index, array) =>
    复制代码
    1.      console.log(val, index, array));
    复制代码


所以 map 回调方法中会传入三个参数,分别是 当前遍历的项,当前索引,及遍历的整个数组。

[h1]原因[/h1]
  1. ParseInt
复制代码
有两个参数:
  1. string
复制代码
  1. radix
复制代码
。 如果提供的基数是虚值,则默认情况下,基数设置为
  1. 10
复制代码

    1. parseInt('11');                => 11
    复制代码
    1. parseInt('11', 2);             => 3
    复制代码
    1. parseInt('11', 16);            => 17
    复制代码
    1. parseInt('11', undefined);     => 11 (radix is falsy)
    复制代码
    1. parseInt('11', 0);             => 11 (radix is falsy)
    复制代码
现在一步一步解析开头的事例。
    1. ['1', '7', '11'].map(parseInt);       => [1, NaN, 3]
    复制代码

    1. // 第一次迭代: val = '1', index = 0, array = ['1', '7', '11']
    复制代码
    1. parseInt('1', 0, ['1', '7', '11']);   => 1
    复制代码
因为
  1. 0
复制代码
是虚值,基数设置为默认值
  1. 10
复制代码
  1. parseInt()
复制代码
只接受两个参数,因此忽略了第三个参数
  1. ['1'、'7'、'11']
复制代码
。以10为基数的字符串
  1. “1”
复制代码
表示数字
  1. 1
复制代码

    1. // 第二次迭代: val = '7', index = 1, array = ['1', '7', '11']
    复制代码
    1. parseInt('7', 1, ['1', '7', '11']);   => NaN
    复制代码
在基数
  1. 1
复制代码
系统中,符号
  1. “7”
复制代码
不存在。与第一次迭代一样,忽略最后一个参数。因此,
  1. parseInt()
复制代码
返回
  1. NaN
复制代码

    1. // Third iteration: val = '11', index = 2, array = ['1', '7', '11']
    复制代码
    1. parseInt('11', 2, ['1', '7', '11']);   => 3
    复制代码
在基数
  1. 2
复制代码
(二进制)系统中,符号
  1. “11”
复制代码
表示数字
  1. 3
复制代码

至此原因已经明了了。 有兴趣可以试着写下下列的打印结果:
    1. ['1', '7', '11'].map(numStr => parseInt(numStr));
    复制代码

我是小智,公众号「大迁世界」作者,对前端技术保持学习爱好者。我会经常分享自己所学所看的干货,在进阶的路上,共勉!

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

本版积分规则

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

下载期权论坛手机APP