最近碰到了[‘1’,’2’,’3’].map(parseInt)这种看似不起眼陷阱却极大的问题。
这乍一看,感觉应该会输出[1,2,3]。但是,实际上并不是我们想的这样。你可以现在打开console,看看输出的结果。
出乎意料结果竟然是[1,NaN,NaN].
至于为什么是这样,下面一步一步的解释。
parseInt() 函数
定义和用法
parseInt()
函数可解析一个字符串,并返回一个整数。
语法
parseInt(string, radix)
参数 | 描述 |
---|---|
string | 必需。要被解析的字符串。 |
radix | 可选。表示要解析的数字的基数。该值介于 2 ~ 36 之间。 如果省略该参数或其值为 ‘0‘,则数字将以 10 为基础来解析。如果它以 ‘”0x”‘ 或 ‘”0X”‘ 开头,将以 16 为基数。 如果该参数小于 2 或者大于 36,则 ‘parseInt()‘ 将返回 ‘NaN‘。 |
返回值
返回解析后的数字。
说明
当参数 radix
的值为 0
,或没有设置该参数时,parseInt()
会根据 string
来判断数字的基数。
举例:
-
如果
string
以"0x"
开头,parseInt()
会把string
的其余部分解析为十六进制的整数。 -
如果
string
以0
开头,那么 ECMAScript v3 允许parseInt()
的一个实现把其后的字符解析为八进制或十六进制的数字。 -
如果
string
以 1 ~ 9 的数字开头,parseInt()
将把它解析为十进制的整数。
提示和注释
注释:只有字符串中的第一个数字会被返回。
注释:开头和结尾的空格是允许的。
提示:如果字符串的第一个字符不能被转换为数字,那么 parseInt()
会返回 NaN
。
实例
在本例中,我们将使用 parseInt()
来解析不同的字符串:
parseInt("10"); // 返回 10 (默认十进制) parseInt("19",10); // 返回 19 (十进制: 10+9) parseInt("11",2); // 返回 3 (二进制: 2+1) parseInt("17",8); // 返回 15 (八进制: 8+7) parseInt("1f",16); // 返回 31 (十六进制: 16+15) parseInt("010"); // 未定:返回 10 或 8
parseInt('123'); //123
当然,如果传入pasreInt()参数并不能被解析成数值表达式,那么函数执行后将返回 NaN。NaN的含义就是Not a Number(不是一个数值)。
var m = parseInt('xyz)
此时m的值应该就是NaN。
Array.prototype.map
map 是ECMAScript 5 的一个内置数值方法.map把函数作为它的参数。map遍历数组中所有的元素,并且为每个元素调用一次这个传入map中的函数,当前元素作为参数传入该函数。map函数调用完毕之后将返回的结果存入一个新数组中。看下面这个例子:
[1,2,3].map(function(value){ return value+1 })
很好理解,最后的结果的应该是[2,3,4].这种调用应该是很常见的。但是有时将已经存在的函数作为参数传入map是无效的啊,比如parseInt.
清楚了parsenInt和map的基本用法之后,再来看看比较容易忽视的问题。
解决方法(前方高能)
再来看看ECMAScript 5 中parseInt 的用法。它可以接受两个参数。第一个参数是要被解析的字符串,第二个参数是要解析成数值的基数。所以, parseInt('ffff',16)
返回的结果是65535,然而
parseInt('ffff',8)
返回的结果是NaN,因为“ffff”并不能解析成一个八进制的数值。如果第二个参数省略了或者是0,parseInt默认为是10进制所以
parseInt("12",10) ;
parseInt("12") ; parseInt("12",0) ;
都会返回数值12。
现在看看map 的定义。map指定传入的函数参数作为callbackfn.规范里指出:“callbackfn调用时需要传入三个参数:元素的值,元素的索引和正在被遍历的对象”。仔细体会一下,原本我们以为对parseInt的三次调用是这样:
parseInt('1')
parseInt('2')
parseInt('3')
然而实际上是这样调用的:
parseInt('1',0,theArray);
parseInt('2',1,theArray); parseInt('3',2,theArray);
这里theArray 就是原始数组[‘1’,’2’,’3’]。
JS函数一般会自动忽视多余的参数,parseInt仅仅需要两个参数,所以我们并不需要担心这个theArray参数对parseInt的影响。
重点来了,第二个参数是如何影响parseInt的?
在第一次调用时,parseInt的第二个参数是0,上面已经说了,所以第一调用 parseInt('1',0)
返回1.
第二次调用第二个参数是1,也是说1作为数值的基础。规范里说的很清楚了,如果基础是非0或者小于2,函数都不会查询字符串直接返回NaN。
第三次调用时,2作为基数。这就意味着字符串将被解析成字节数,也就是仅仅包含数值0和1。parseInt的规范第十一步指出,它仅尝试分析第一个字符的左侧,这个字符还不是要求基数的有效数字。这个字符串的第一个字符是“3”,它并不是基础基数2的一个有效数字。所以这个子字符串将被解析为空。第十二步说了:如果子字符串被解析成空了,函数将返回为NaN。
所以这里的结果就应该是[1,NaN,NaN].
这里问题所在就是容易忽视parseInt是需要两个参数的。map中有三个参数。所以这里结合起来,就导致了上面问题。
换个方式写:
['1','2','3'].map(function(value){ return parseInt(value) })
这样就不会出错。
当然,我们也可以写:
['1','2','3'].map(Number)