为什么parseInt用Array#map产生NaN?


290

来自Mozilla开发人员网络

[1,4,9].map(Math.sqrt)

将产生:

[1,2,3]

为什么这样做:

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

产生这个:

[1, NaN, NaN]

我已经在Firefox 3.0.1和Chrome 0.3中进行了测试,并且免责声明,我知道这不是跨浏览器功能(没有IE)。

我发现以下将达到预期的效果。但是,它仍然不能解释的错误行为parseInt

['1','2','3'].map(function(i){return +i;}) // returns [1,2,3]

15
对于懒惰的:使用,.map(parseFloat)因为它只需要一个参数。

63
或使用.map(Number)
Nikolai

2
如果想要不带手动滚动功能的整数,则可以使用arr.map(Math.floor)。
dandavis,2014年

@Nikolai user669677很棒的建议!我会在答案中投票赞成
BiAiB

谁能解释为什么parseInt会正确解析第一个数字并为除第一个索引
之外的

Answers:


478

中的回调函数Array.map具有三个参数:

在您链接到的同一Mozilla页面上:

回调使用三个参数来调用:元素的值,元素的索引和要遍历的Array对象。”

因此,如果调用一个parseInt实际上需要两个参数的函数,则第二个参数将是元素的索引。

在这种情况下,您最终parseInt依次调用基数0、1和2。第一个与不提供参数相同,因此默认基于输入(在本例中为10)。以1为底的数字是不可能的,以3为底的数字不是有效的数字:

parseInt('1', 0); // OK - gives 1
parseInt('2', 1); // FAIL - 1 isn't a legal radix
parseInt('3', 2); // FAIL - 3 isn't legal in base 2 

因此,在这种情况下,您需要包装函数:

['1','2','3'].map(function(num) { return parseInt(num, 10); });

或使用ES2015 +语法:

['1','2','3'].map(num => parseInt(num, 10));

(在两种情况下,最好如图所示显式提供一个基数parseInt,因为否则它会根据输入来猜测基数。在某些较旧的浏览器中,前导0会导致它猜测八进制,这往往是有问题的。它仍然会如果字符串以0x。开头,则猜测为十六进制。)


25

map正在传递第二个参数,该参数(在许多情况下)弄乱了parseIntradix的基数参数。

如果您使用下划线,则可以执行以下操作:

['10','1','100'].map(_.partial(parseInt, _, 10))

或不带下划线:

['10','1','100'].map(function(x) { return parseInt(x, 10); });


18

您可以使用Number作为iteratee函数来解决此问题:

var a = ['0', '1', '2', '10', '15', '57'].map(Number);

console.log(a);

如果没有新的运算符,则Number可用于执行类型转换。但是,它与parseInt有所不同:它不分析字符串,如果数字不能转换,则返回NaN。例如:

console.log(parseInt("19asdf"));
console.log(Number("19asf"));


11

我要押注的是,parseInt的第二个参数基数有点时髦。我不知道为什么使用Array.map会破坏它,而不是直接调用它时却没有。

//  Works fine
parseInt( 4 );
parseInt( 9 );

//  Breaks!  Why?
[1,4,9].map( parseInt );

//  Fixes the problem
[1,4,9].map( function( num ){ return parseInt( num, 10 ) } );

1
如果提供的字符串以0字符开头,则parseInt仅假定八进制。
Alnitak

哦,是的。它尝试根据输入来“猜测”基数。对于那个很抱歉。
彼得·贝利

3

您可以使用箭头功能ES2015 / ES6,只需将数字传递给parseInt。基数的默认值为10

[10, 20, 30].map(x => parseInt(x))

或者,您可以显式指定基数以提高代码的可读性。

[10, 20, 30].map(x => parseInt(x, 10))

在上面的示例中,基数显式设置为10


1

另一个(有效的)快速解决方案:

var parseInt10 = function(x){return parseInt(x, 10);}

['0', '1', '2', '10', '15', '57'].map(parseInt10);
//[0, 1, 2, 10, 15, 57]

0

parseInt为此,应避免恕我直言。您可以包装它以使其在以下情况下更安全:

const safe = {
  parseInt: (s, opt) => {
    const { radix = 10 } = opt ? opt : {};
    return parseInt(s, radix);
  }
}

console.log( ['1','2','3'].map(safe.parseInt) );
console.log(
  ['1', '10', '11'].map(e => safe.parseInt(e, { radix: 2 }))
);

lodash / fp默认将iteratee参数设置为1以避免这些陷阱。我个人发现这些变通办法会创建尽可能多的错误。parseInt我认为将黑名单推荐给更安全的实现是一种更好的方法。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.