详解 JavaScript 中的 parseInt 函数

之前在 Stack Overflow 看到题目 `parseInt(1/0, 19),代码执行的结果是 18,令人出乎意料。虽然这个题目是很少见到的情况(0作为被除数、采用19进制),但这个题目是一个很好的了解JavaScript parseInt 函数的机会。

要解答 parseInt(1/0, 19)这个题目,要理解两个知识点:

  1. 1/0 的值是什么?
  2. parseInt 函数是怎么样将一个 String 转化成Int

0 作为被除数

JavaScript里的所有算术运算采用的是IEEE 754 Standard。在 IEEE 754 Standard 中定义当被除数为 0 时,结果是(正/负)无穷大,无穷大在 JavaScript 中使用 Infinity 代表。所以表达式 1/0 的结果是正无穷大,即:Infinity

parseInt函数

parseInt的定义是:接收一个字符串并返回一个基于指定基数的整型值。语法为:

1
parseInt(string, radix);

parseInt 执行的步骤可以分为四个:

  • 初步解析 string 参数
  • 解析 radix 基数参数
  • 根据 radix 再次解析字符串
  • 返回结果

执行步骤说明如下:

初步解析字符串

函数指定第一个参数是字符串,但存在着传入的参数是非字符串类型的可能性。所以这一步中,首先将 string 参数转换成 字符串类型。
接着,剔除掉转换之后的字符串前面的空白符,生成新的字符串string1。若字符串为空,则返回NaN。

parseInt(1/0, 19) 中,1/0 表达式的值为 InfinityInfinity 是一个数值,所以,在这一步将被转换成 "Infinity

接着,判断string1是否包含+或者-符号,设定sign变量为+/-,并将符号从字符串中剔除,生成字符串string2。
这一步执行完毕之后,获取新的字符串 string2,没有包含空格以及+/-符号。

解析基数参数

与string参数相同,radix可能非数值,所以首先将radix转换成整型radix1。
在JavaScript中,radix的取值可能有两种:等价于0的值(比如undefined、null、false、””以及0本身),[2-36]。若radix1的取值不在这两者之中,返回NaN。
这一步执行完毕,获取新的基数值 radix1。

再次解析字符串

基数的范围设定在[2-36],其中前十位使用[0-9]表示,[10-35]位使用英语字母表示,即[a-z]或者[A-Z]。从这里可以看出基数的大小决定着字符串的取值有效范围[0-9a-ZA-Z]。例如:

parseInt(“a”, 10);
// 在基数为10的情况下,”i”不能被转换成有效的数值,所以返回的是NaN

parse(“a”, 11”);
// 基数更改为11,”a”可以被转换成10
由于基数会影响解析的行为,所以这里需要再次解析字符串。此时将逐个检查字符串中的字符,当遇到字符在基数指定的有效范围之外,解析停止,字符串中后续的字符将被丢弃,生成新的字符串string3。若string3为空,返回NaN。
需要注意的是这一步中,基数有一个特殊的取值情况是:

  • 当基数为16时,字符串中若开头为”0x”/“0X”,这两个字符将被过滤掉。因为16进制的表示法为“oxNN”。

这一步执行完毕之后,获取新的字符串 string3。

返回结果

最后一步,将上一步中获取的string3转换成数值表示,如”a”在11进制中将被转换成10;然后将符号sign添加到数值上,并返回结果

再看 parseInt(1/0,19)

从上面的执行步骤来看,parseInt(1/0, 19)即是parseInt(“Infinity”, 19);在基数为19的情况下,字符串中第一个字符”I”是有效的数值,但第二个字符”n”无效,所以又变成了parseInt(“I”, 19)。”i”所对应的数值如下所示:

0 1 2 3 4 5 6 7 8 9 a  b  c  d  e  f  g  h  i
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

所以结果是:18

发散

另外有一个道题目,将数组所有元素的字符串转换成整型值:

1
["10", "10", "10", "10", "10"].map(parseInt)

map 函数的语法为:[].map(fn)

map 函数在数组的每个元素上执行fn,返回在每个元素上执行fn结果组成的数组。fn函数接收三个参数:元素值、元素索引值、数组本身。所以上面的调用执行的是:

1
2
3
4
5
6
7
[
parseInt("10", 0); // 基数0, 即默认10进制
parseInt("10", 1); // 基数1无效,返回NaN
parseInt("10", 2); // 基数2, 10表示2
parseInt("10", 3); // 基数3, 10表3
parseInt("10", 4); // 基数4, 10表示4
]

所以,结果是:[10, NaN, 2, 3, 4]

潜在的 Bug

如果使用JSHint校验parseInt(1/0)的时候会提示:

1
2
Line 2: parseInt(1/0);
Missing radix parameter.

提示需要指定 radix(第二个参数)。举个例子:

1
parseInt("08");

8进制和16进制的数字以”0”开头(16进制是”0X”),所以在有些脚本解释器中(IE8浏览器)可能误以8进制来解析”08”这个字符串。在8进制中,”08”是个无效的值,所以无法得到预期值。为了保证结果的准确性,建议在使用parseInt的时候,指明基数。

参考:

0%