你不知道的 parseInt?
前言
parseInt
用于解析一个字符串并返回指定基数的十进制整数,使用语法parseInt(string, radix)
。radix
表示字符串的基数,值为2 ~ 36
。
提起 parseInt
,小包脑海中不由自主会浮现两个东西,其一是 parseInt
的截断用法;其二就是烂大街的 ['1', '2', '3'].map(parseInt)
面试题。尤其是后面这个面试题,最开始简直就是魂牵梦萦的噩梦。但这真的是 parseInt
的全部吗?下面和小包一起来探索 parseInt
的重重惊喜吧。
惊喜一: parseInt (0.0000005) === 5 为 true,大大的疑惑?
小小的脑袋,大大的疑惑,parseInt
转换后的数字为什么会是 5
呐,不应该是 0
吗?(注意传入参数为浮点类型,不是字符串)
我们来看一下 MDN
对 parseInt
语法的解释: parseInt
方法接收两个参数待解析值 string
,基数 radix
。string
参数如果不是字符串,则会调用其 toString
方法转换为字符串。因此遇到非字符串解析值,parseInt
内部会有两部操作:
Step1: 调用 toString 方法
(0.5).toString(); // '0.5' (0.05).toString(); // '0.05' (0.005).toString(); // '0.005' (0.0005).toString(); // '0.0005' (0.00005).toString(); // '0.00005' (0.000005).toString(); // '0.000005' (0.0000005).toString(); // '5e-7'
注意上面的输出,我们可以发现当数字过小时,toString
输出的结果是科学计数法形式。
Step2: 截断操作
引用一段来自 StackOverflow 的解释:<br> parseInt 只能将字符串的前导部分解释为整数值;它忽略任何不能被解释为整数的代码单元,并且不会有忽略指示。
因此答案只会返回 5
,其余的 e-7
被忽略。
注意事项:
如果将要解析的值放入字符串中,那么上面这种惊喜就不会出现了:
parseInt("0.0000005"); // 0 parseInt("0.000000005"); // 0
从这个惊喜我们可以收获一个警示:尽可能要避免隐式类型转换。
扩展
数字过小时,调用 toString
方法,会返回科学技术法格式表示;数字过大时,同样也会有类似的情况。
parseInt(999999999999999999999) // 1 // (999999999999999999999).toString() // '1e+21'
但小包在测试过程中发现一些临界值,比较奇怪,有兴趣大家可以去探讨一下。
// 10000000000000000 parseInt(9999999999999999)
惊喜二:radix 默认值是 10 吗,何种情况返回值是 NaN?
下面我们就几种情况对这个惊喜进行分析:
2.1 radix 值为 undefined、0 或未指定的
当 radix
值为 undefined、0
或未指定的,那 JavaScript
会如何处理这种情况:
-
如果输入的
string
以0x
或0X
开头,那么radix
会被假定为16
,字符串的其他部分按照十六进制来解析。 -
如果输入的
string
以0
开头,ES5
规定使用十进制,但并非所有的浏览器都支持,因此使用parseInt
时,需要指定radix
-
如果输入的
string
以其他任何值开头,radix
值为10
我们用 google
浏览器体验一下上述规则。
parseInt("0x16"); // 22 parseInt("0888"); // 888 parseInt("123px"); // 123
2.2 radix 值小于 2 或 大于 36
radix
参数的值为 2 ~ 36
,当 radix
小于 2
或 大于 36
(不包含 0
),返回值为 NaN
parseInt("123px", 1); // NaN parseInt("123px", 38); // NaN parseInt("123px", -1); // NaN
2.3 待转换字符串中,所有的可转换数字都不小于 radix 值
举个栗子: 例如 radix
值为 2
(二进制),而待转换字符串为 '3456'
,二进制内只有 0、1
是基本算符,因此字符串 '3456'
无法转换成二进制,返回值为 NaN
。
parseInt("3456", 2); // NaN parseInt("45px", 3); // NaN
2.4 总结
学习完上面几种情况,我们就可以来回答惊喜二的问题了。
-
radix
默认值是10
吗?答案: 不是,radix
还会考虑string
参数,如果string
以0x(0X)
开头,radix
为16
;若以0
开头,不同的浏览器有可能有不同的值;其余情况radix
为10
-
何种情况返回值是
NaN
? -
radix
值小于2
或 大于36
-
待转换字符串中,所有的可转换数字都不小于
radix
值(例如parseInt('345', 2)
) -
第一个非空格字符不能转换为数字(例如
parseInt('a123')
)
惊喜三:radix 参数引出的面试题
3.1 [‘1’, ‘2’, ‘3’].map(parseInt)
map 函数对数组的每个元素执行回调函数,并返回含有运算结果的新数组。
// val 为数组值 // index 为当前数组索引 // _arr 为当前数组 arr.map((val, index, _arr) => {});
因此 ['1', '2', '3'].map(parseInt)
就等同于下面的代码:
[parseInt("1", 0), parseInt("2", 1), parseInt("3", 2)];
-
parseInt("1", 0)
符合2.1
,radix
为0
,且string
以字符1
开始,radix
值为10
,值为1
。 -
parseInt("2", 1)
符合2.2
,radix
小于2
,返回NaN
-
parseInt("3", 2)
符合2.3
,待转换字符串中,所有的可转换数字大于radix
值,返回NaN
因此该面试题的答案为 [1, NaN, NaN]
3.2 [’10’, ’10’, ’10’, ’10’].map(parseInt)
有了上面的基础,这个题目就好理解了,我们先将这个方法等价转化一下:
[parseInt("10", 0), parseInt("10", 1), parseInt("10", 2), parseInt("10", 3)];
相当于将 '10'
分别转化为十进制,一进制,二进制,三进制,因此该面试题的结果是 [10, NaN, 2, 3]
。
惊喜四:parseInt 的旁支末节
parseInt
方法除了上面提到的惊喜外,还有一些旁支末节,也需要注意一下:
-
parseInt
允许前导和后置空格,在进行转换前,会首先删除字符串的前导和后置空格。
// 允许前导和后置空格 parseInt(" 123 "); // 123 // 中间出现的空格会被当做截断符 parseInt("123 456 "); // 123
-
parseInt
可以理解正负号
parseInt("+123"); // 123 parseInt("-123"); // -123 // 只能理解第一个正负号,第二个非数字字符,因此返回 NaN parseInt("+-123"); // NaN
-
使用
parseInt
转换BigInt
会损失精度
parseInt
将 BigInt
转换为 Number
,并在这个过程中失去了精度。这是因为拖尾的非数字值,包括 "n"
,会被丢弃。
let bigInt = 111111111111111111111111n; parseInt(bigInt); // 1.1111111111111111e+23 let bigInt2 = 11n; parseInt(bigInt2); // 11
博文仓库
传送门: 战场小包的博客库
别忘了给小包点个 🌟。
后语
我是 战场小包 ,一个快速成长中的小前端,希望可以和大家一起进步。
如果喜欢小包,可以在 InfoQ 关注我,同样也可以关注我的小小公众号——小包学前端。
一路加油,冲向未来!!!
疫情早日结束 人间恢复太平
本文文字及图片出自 InfoQ
你也许感兴趣的:
- 【外评】电脑从哪里获取时间?
- 【外评】为什么 Stack Overflow 正在消失?
- Android 全力押注 Rust,Linux 却在原地踏步?谷歌:用 Rust 重写固件太简单了!
- 【外评】哪些开源项目被广泛使用,但仅由少数人维护?
- 【外评】好的重构与不好的重构
- C 语言老将从中作梗,Rust for Linux 项目内讧升级!核心维护者愤然离职:不受尊重、热情被消耗光
- 【外评】代码审查反模式
- 我受够了维护 AI 生成的代码
- 【外评】Linux 桌面市场份额升至 4.45
- 【外评】作为全栈开发人员如何跟上 AI/ML 的发展?
你对本文的反应是: