之前在 Stack Overflow 看到题目 `parseInt(1/0, 19),代码执行的结果是 18,令人出乎意料。虽然这个题目是很少见到的情况(0作为被除数、采用19进制),但这个题目是一个很好的了解JavaScript parseInt
函数的机会。
要解答 parseInt(1/0, 19)
这个题目,要理解两个知识点:
1/0
的值是什么?parseInt
函数是怎么样将一个String
转化成Int
?
0 作为被除数
JavaScript里的所有算术运算采用的是IEEE 754 Standard。在 IEEE 754 Standard 中定义当被除数为 0 时,结果是(正/负)无穷大,无穷大在 JavaScript 中使用 Infinity
代表。所以表达式 1/0
的结果是正无穷大,即:Infinity
。
parseInt函数
parseInt
的定义是:接收一个字符串并返回一个基于指定基数的整型值。语法为:
|
|
parseInt
执行的步骤可以分为四个:
- 初步解析 string 参数
- 解析 radix 基数参数
- 根据 radix 再次解析字符串
- 返回结果
执行步骤说明如下:
初步解析字符串
函数指定第一个参数是字符串,但存在着传入的参数是非字符串类型的可能性。所以这一步中,首先将 string 参数转换成 字符串类型。
接着,剔除掉转换之后的字符串前面的空白符,生成新的字符串string1。若字符串为空,则返回NaN。
parseInt(1/0, 19)
中,1/0
表达式的值为 Infinity
。Infinity
是一个数值,所以,在这一步将被转换成 "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
发散
另外有一个道题目,将数组所有元素的字符串转换成整型值:
|
|
map
函数的语法为:[].map(fn)
。
map
函数在数组的每个元素上执行fn,返回在每个元素上执行fn结果组成的数组。fn函数接收三个参数:元素值、元素索引值、数组本身。所以上面的调用执行的是:
|
|
所以,结果是:[10, NaN, 2, 3, 4]
。
潜在的 Bug
如果使用JSHint校验parseInt(1/0)的时候会提示:
|
|
提示需要指定 radix(第二个参数)。举个例子:
|
|
8进制和16进制的数字以”0”开头(16进制是”0X”),所以在有些脚本解释器中(IE8浏览器)可能误以8进制来解析”08”这个字符串。在8进制中,”08”是个无效的值,所以无法得到预期值。为了保证结果的准确性,建议在使用parseInt的时候,指明基数。
参考: