JavaScript 中 Array#map 和 parseInt 组合使用的陷阱
by Xueqiao Xu
at 2012-02-11 13:15:05
original http://typedef.me/2012/02/11/javascript-array-map-parseint-pitfall
将一个形如 ['1', '2', '3'] 的数组转换为 [1, 2, 3] ,很多人也许会不加思索的写出下面的代码:
['1', '2', '3'].map(parseInt);
上面这个代码看上去十分正确,而且非常优美,没有任何多余的噪音。但其返回结果却令人吃惊:
> ['1', '2', '3'].map(parseInt); [ 1, NaN, NaN ]
“WTF !!!!为什么第一个元素是正确的,而后面的会是
NaN
?”
然后继续实验:
> ['1', '1', '1', '1'].map(parseInt); [ 1, NaN, 1, 1 ]
“喂喂,中间的那位
NaN
同学,你走错片场了吧。”
究其原因,其实是由两部分组成的:
map
的回调函数的第二个参数为数组下标parseInt
的第二个参数为转换进制
看到上面这两点,熟练 js 的同学应该就会恍然大悟了。如果还没明白的话也没关系,下面是详细的解释:
Array#map
首先,map 方法所期待的回调函数在调用时接受三个参数,分别为:
- 当前元素的值
- 当前元素的索引(下标)
- 被遍历的数组
一个典型的 map 实现如下:
Array.prototype.map = function map(fun) { var length = this.length >>> 0; var result = Array(length); var thisp = arguments[1]; for (var i = 0; i < length; i++) { if (i in this) result[i] = fun.call(thisp, this[i], i, this); } return result; };
调用示例:
['a', 'b', 'c'].map(function(v, i, a) { console.log(v, i, a); }); // prints // a 0 [ 'a', 'b', 'c' ] // b 1 [ 'a', 'b', 'c' ] // c 2 [ 'a', 'b', 'c' ]
parseInt
parseInt
可以接受两个参数,其中第二个参数(可选)为转换的进制
例如,以 8 进制解析 12
> parseInt('12', 8) 10
而当第二个参数不存在或为0
时:
- 如果字符串以
0x
或0X
开头,则以 16 进制解析 - 如果以
0
开头,则以 8 进制解析 - 否则以 10 进制解析
- 解析失败返回 NaN
因此,当我们组合使用 map
和 parseInt
的时候,每个元素的下标成为了解析它时的进制:
['1', '2', '3'].map(function(v, i) { console.log('parseInt("' + v + '", ' + i + ') gives ' + parseInt(v, i)); }); // prints // parseInt("1", 0) gives 1 // parseInt("2", 1) gives NaN // parseInt("3", 2) gives NaN
注意上面结果的最后两项,以1进制来解析 "2"
和以2进制来解析 "3"
都是失败的,因此返回 NaN
。
正确的做法应当为:
['1', '2', '3'].map(function(v) { // 只传入元素的值 return parseInt(v, 10); // 指定以 10 为底,这是一个好习惯 })
至此,原因应当相当清晰了。同时,也当记住,除了 parseInt
外,任何可以接受两个或两个以上参数的函数在配合 map
使用的时候都应当多加注意。