为啥 parseInt 使用 Array#map 产生 NaN?

Posted

技术标签:

【中文标题】为啥 parseInt 使用 Array#map 产生 NaN?【英文标题】:Why does parseInt yield NaN with Array#map?为什么 parseInt 使用 Array#map 产生 NaN? 【发布时间】:2021-12-12 21:41:58 【问题描述】:

来自Mozilla Developer Network:

[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]

【问题讨论】:

对于懒惰的:使用.map(parseFloat),因为它只需要一个参数。 或使用.map(Number) 如果你想要没有手动函数的整数,你可以 arr.map(Math.floor)。 @Nikolai user669677 很棒的建议!我会在 anwser 中对此表示赞成 谁能解释一下为什么 parseInt 能正确解析第一个数字,但除了第一个索引之外会出错 【参考方案1】:

你可以这样解决这个问题:

array.map(x => parseInt(x))

例子:

var arr = ["3", "5", "7"];

console.log(
  arr.map(x => parseInt(x))
);

【讨论】:

【参考方案2】:

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

来自您链接到的同一 Mozilla page:

使用三个参数调用回调:元素的值、元素的索引和被遍历的 Array 对象。"

所以如果你调用一个函数parseInt,它实际上需要两个参数,第二个参数将是元素的索引。

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

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开头,它仍然会猜测十六进制。)

【讨论】:

【参考方案3】:

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 列入黑名单以支持更安全的实施是一种更好的方法。

【讨论】:

【参考方案4】:

您可以使用Number 作为迭代函数来解决这个问题:

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

console.log(a);

没有new操作符,Number可以用来进行类型转换。但是,它与 parseInt 不同:它不解析字符串,如果数字无法转换,则返回 NaN。例如:

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

【讨论】:

【参考方案5】:

您可以使用箭头函数 ES2015/ES6 并将数字传递给 parseInt。基数的默认值为 10

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

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

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

在上面的示例中,基数明确设置为 10

【讨论】:

【参考方案6】:

另一个(工作)快速修复:

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

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

【讨论】:

【参考方案7】:

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

如果你使用下划线,你可以这样做:

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

或不带下划线:

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

【讨论】:

【参考方案8】:

我敢打赌,parseInt 的第二个参数 radix 会很奇怪。我不知道为什么使用 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 )  );

【讨论】:

parseInt 仅在提供的字符串以 0 字符开头时假定为八进制。 哦,是的……没错。它试图根据输入“猜测”基数。对此感到抱歉。

以上是关于为啥 parseInt 使用 Array#map 产生 NaN?的主要内容,如果未能解决你的问题,请参考以下文章

parseInt 的第二个参数

JS面试题:[1, 2, 3].map(parseInt)

Web 前端陈年烂题:[‘1‘, ‘2‘, ‘3‘].map(parseInt) 结果是什么?

js , map中的坑

为啥 map.values().stream() 比 Array.stream(array) 慢得多

为啥在 innerHTML 中使用的 Array#map 输出中的额外逗号?