逃离 parseInt 的陷阱

1,087 阅读3分钟

李逵与李鬼

请看下面两段代码:

parseInt(0.0000000789); // 7
parseInt('0.0000000789'); // 0

小朋友,你是否有很多问号?

分析

明显地,这两段代码的区别是,一个输入是 Number 类型,一个是 String 类型。parseInt 的第一个参数要求是 String 类型的。所以第二段代码输出的结果是正常的,而第一段代码出现了异常。那么出现异常的地方是哪里呢?

假如 parseInt 的参数是非 String 类型,那么将会调用 toString 方法,将参数转化为字符串,而恰恰,toString 方法将 Number 转化为 Srting 是有一定的规则的,当需要转化的数字过小(小数点后10位及10位以上) 小数点前的数组多于21位或小数点后连续跟着5个零以上(暂无找到资料支持,但实践上是这样),将会转化为科学计数法 ,如:

0.000000789; // 7.89e-7
1234567890123456789012; // 1.2345678901234568e+21
(0.0000000789).toString(); // "7.89e-8"
(0.00000789).toString(); // "0.00000789"

其实 parseInt(0.0000000789) 等同于 parseInt((0.0000000789).toString()) 等同于 parseInt("7.89e-8") ,所以答案就是 7 了。虽然结果诡异了点,但了解过程后,其实还是很有趣的。

现在,你明白,为啥 parseInt(0.0000000789) 的结果是 7 了吧。

面试题之一:组队刷 parseInt 咯

请看代码:

[1,2,3].map(parseInt);

此处,有陷阱!

首先,了解一下,map :

var new_array = arr.map(function callback(currentValue[, index[, array]]) {
// Return element for new_array 
}[, thisArg])

callback 方法将会传入一个参数 currentValue ,是callback 数组中正在处理的当前元素;两个可选参数: indexcallback 数组中正在处理的当前元素的索引; arraymap 方法调用的数组。

再了解一下 parseInt :

parseInt(string, radix);

string 是要被解析的值。如果参数不是一个字符串,则将其转换为字符串(使用 ToString 抽象操作)。字符串开头的空白符将会被忽略。

radix 表示字符串的基数, 从 236。例如指定 16 表示被解析值是十六进制数。请注意,10不是默认值!

所以 :

[1,2,3].map(parseInt);
// 等同于
[1,2,3].map((item, index) => parseInt(item, index)); // [1, NaN, NaN]
// item 是 1 时,callback -> parseInt(1, 0) ,结果是 1
// item 是 2 时,callback -> parseInt(2, 1) ,结果是 NaN
// item 是 3 时,callback -> parseInt(3, 2) ,结果是 NaN
// parseInt(3, 2) 表示是以 2 进制的方式转换数值,而 2 进制接受的字符串值只有 0 和 1
// 其实就相当于 10 进制时,输入16进制的值去转换: parseInt('a', 10)
// 所以结果就是 NaN 了

公道话

咳咳,讲个公道话,上面两个 parseInt 陷阱,非 parseInt 之错。第一个是因为参数不是 String 类型的,所以调用了 toString 方法转换,得到不是理想值,但 parseInt 依据它的规则,很好地执行了。而第二个陷阱,罪魁祸首应是 map ,应当多注意 map 的参数。

只要谨记并遵守 parseInt 方法的参数类型和调用规则,就不会轻易掉到陷阱里面去了。

参考

6.1.6.1.20 Number::toString ( x )

JS中parseInt()、Numer()深度解析

Array.prototype.map()

parseInt