HarmonyOS开发者限时福利来啦!最高10w+现金激励等你拿~ 了解详情
写点什么

奇怪的 JavaScript:map 和 parseInt 的反常应用

  • 2019-09-26
  • 本文字数:2723 字

    阅读完需:约 9 分钟

奇怪的JavaScript:map和parseInt的反常应用

在 JavaScript 中,当使用 map 和 parseInt 将字符数组转换为整数时,却出现了奇怪的现象。控制台输入 [‘1’, ‘7’, ‘11’].map(parseInt)后,得到的结果是 [1, NaN, 3] ,而不是 [1, 7, 11] 。本文将通过解释一些相关的 JavaScript 概念,解答出现这种状况的原因。



JavaScript 很奇怪。不相信吗?尝试使用 map 和 parseInt 将字符数组转换为整数。打开控制台(Chrome 上的快捷键是 F12),粘贴下面的代码,然后按回车键(或将焦点放到下面)。我们得到的不是一个整数数组 [1, 7, 11] ,而是 [1, NaN, 3] 。为什么呢?


['1', '7', '11'].map(parseInt);
复制代码


要想了解到底发生了什么,我们首先要讨论一些 JavaScript 的概念。如果你想要一个 TLDR(Too Long; Didn’t Read:太长了,不想读),我在本文的末尾提供了一个快速总结。

真 & 假

下面是 JavaScript 中的一个简单 if-else 语句:


if (true) {    // 此处总会运行} else {    // 此处不会运行到}
复制代码


在本例中,if-else 语句的条件为 true,因此它总会执行 if 块,而忽略 else 块。这是一个很简单的例子,因为 true 是一个布尔值。如果我们把一个非布尔值作为条件呢?


if ("hello world") {    // 这将会运行吗?    console.log("Condition is truthy");} else {    // 还是运行这里?    console.log("Condition is falsy");}
复制代码


尝试在开发人员的控制台(在 Chrome 上按 F12)中运行此代码。我们会发现 if 块运行了。这是因为字符串对象“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 9 10
复制代码


当我们从 0 数到 9 时,每个数字(0-9)都有不同的符号。然而,一旦达到 10,我们需要两个不同的符号(1 和 0)来表示这个数字。这是因为我们的十进制计数系统的基数(或基础)是 10。


基数是一个最小的数字,它只能由多个符号来表示。不同的计数系统有不同的基数,因此,相同的数字在不同的计数系统中,可以表示不同的数字。


十进制   二进制    十六进制基数 =10  基数 =2   基数 =160         0         01         1         12         10        23         11        34         100       45         101       56         110       67         111       78         1000      89         1001      910        1010      A11        1011      B12        1100      C13        1101      D14        1110      E15        1111      F16        10000     1017        10001     11
复制代码


例如,观察上表,我们可以看到在不同的计数系统中,相同的数字 11 可以表示不同的数字。如果基数是 2,那么它指的是数字 3。如果基数是 16,那么它指的是数字 17。


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

函数的参数

在 JavaScript 中,函数可以用任意数量的参数来调用,即使该数量不等于声明函数时参数的数量。缺少的参数将被视为未定义,多余的参数将被忽略,但是它们会被存储在类似数组的参数对象中。


function foo(x, y) {    console.log(x);    console.log(y);}foo(1, 2); // log 输出 1, 2foo(1); // log 输出 1, undefinedfoo(1, 2, 3); // log 输出 1, 2
复制代码

map()

到了我们要讲的部分了!


Map 是数组原型中的一个方法,它返回一个新的数组,其中包含将原始数组的每个元素传递到函数中的结果。例如,下面的代码将数组中的每个元素乘以 3:


function multiplyBy3(x) {    return x * 3;}const result = [1, 2, 3, 4, 5].map(multiplyBy3);console.log(result); // log 输出 [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');                => 11parseInt('11', 2);             => 3parseInt('11', 16);            => 17parseInt('11', undefined);     => 11 (基数为假)parseInt('11', 0);             => 11 (基数为假)
复制代码


现在让我们一步一步地运行我们的示例。


['1', '7', '11'].map(parseInt); => [1, NaN, 3]第一次第一次迭代 index = 0, array = ['1', '7', '11']parseInt('1', 0, ['1', '7', '11']); => 1
复制代码


因为 0 是假,所以基数设置为默认值 10。parseInt() 只接受两个参数,因此会忽略第三个参数 [‘1’, ‘7’, ‘11’] 。以 10 为基数的字符串 ‘1’ 表示数字 1。


// 第二次迭代 index = 1, array = ['1', '7', '11']parseInt('7', 1, ['1', '7', '11']); => NaN
复制代码


在基数为 1 的系统中,符号 ‘7’ 不存在。与第一次迭代一样,最后一个参数将被忽略。因此,parseInt() 返回 NaN。


// 第三次迭代, 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 是假。


因此,下面的代码能按预期工作:


['1', '7', '11'].map(numStr => parseInt(numStr));
复制代码


原文链接:


https://medium.com/dailyjs/parseint-mystery-7c4368ef7b21


2019-09-26 16:161602
用户头像

发布了 297 篇内容, 共 191.7 次阅读, 收获喜欢 596 次。

关注

评论 2 条评论

发布
用户头像
interesting
2019-09-27 11:34
回复
用户头像
基础知识掌握不牢,还想当然着用
2019-09-27 10:15
回复
没有更多了
发现更多内容

观测云:技术创新与敏捷迭代的先锋

可观测技术

数据分析

全栈监控:一目了然的 IT 管理

可观测技术

监控

2024快应用开发者大会:携手AI,共筑未来智慧服务新生态

科技热闻

10亿数据秒级查询,西南证券与镜舟科技合作,构建极速、高效数据平台

镜舟科技

数据库 大数据 数据分析 StarRocks

MySQL5.7 中连续 Crash 引发 GTID 丢失

爱可生开源社区

MySQL 数据库

共情财务业务一体化管理,成为企业合作伙伴的拥护者

智达方通

企业管理 企业转型 全面预算管理 财务管理 财务转型

如何使用C# 获取Windows系统信息以及CPU、内存和磁盘使用情况

哦豁完蛋了

代理IP在社媒营销中的重要作用

IPIDEA全球HTTP

告别杂音,从 AI 音频降噪开始

七牛云

AI降噪

万界星空科技自动化运维管理---设备管理

万界星空科技

数据采集 mes 自动化运维 设备管理 万界星空科技

华为大咖说丨当“AI大潮”来袭时,你是否还在“裸泳”?

华为云PaaS服务小智

人工智能 华为云

SDN与SD-WAN的交集和区别

Ogcloud

SD-WAN 企业组网 SD-WAN组网 SD-WAN服务商 SDWAN

2024 年 7 月公链行业研报:市场波动中 Solana 表现抢眼,Layer 2 竞争白热化

Footprint Analytics

比特币 以太坊 公链 #区块链

功能发布-事件分析之漏斗分析

ClkLog

数据分析 埋点 开源软件

清晰易懂二分查找算法 你确定不看吗?

不在线第一只蜗牛

Java Python 算法

断崖式领先!百度搜索登顶AI产品榜国内第一

Geek_2d6073

适合新手进行接口与自动化测试练习的推荐网站!!!

EquatorCoco

接口 自动化测试

首部顶级AI科学家创作的纯正科幻小说,一个元宇宙和AI时代的全新科学幻想!

博文视点Broadview

TiDB v7.5.3 发版,听说升级后又可以躺平两年

TiDB 社区干货传送门

版本升级 新版本/特性解读 7.x 实践

手摸手教你,从0到1开发一个Chrome浏览器插件

左诗右码

Chrome Extension

音乐制作工具:Studio One 6 (Win&Mac) 激活版

你的猪会飞吗

Studio One 许可证 Studio One 破解 Studio One 6下载

科大讯飞哪一款适合初高中 科大讯飞T20Pro值得买吗

妙龙

学习机

科大讯飞AI学习机P30 值得入手吗 功能介绍

妙龙

个人开源项目商业化经验分享

秦少卫

开源图片编辑器 开源vue图片编辑器 vue图片编辑器

头部保险公司国寿财核心系统采用 TiDB 实现信创替换并实现重大突破

TiDB 社区干货传送门

实践案例

北京银行如何利用 TiDB 实现20个关键业务系统的高效运行

TiDB 社区干货传送门

什么是CSPO及成为CSPO的好处?

ShineScrum捷行

关于低代码这一技术的杂谈

秃头小帅oi

天融信与涛思数据达成战略合作,共筑数据安全新高地

TDengine

从概念到落地:全面解析DApp项目开发的核心要素与未来趋势

区块链软件开发推广运营

dapp开发 区块链开发 链游开发 NFT开发 公链开发

在一串字符串中Java使用正则匹配电话号码的方法

EquatorCoco

Java MySQL 开发语言

奇怪的JavaScript:map和parseInt的反常应用_大前端_Eric Tong_InfoQ精选文章