DEBUG.WIKI
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
雨云;给项目一台云服务器
RackNerd;$21.99/年 · 1GB KVM

JavaScript 的 map(parseInt) 把数字字符串转换成 NaN

map 会把数组索引传给 parseInt 的进制参数,导致 NaN 或错误数值。用包装函数固定进制,并区分整数前缀解析与完整输入验证。

单独转换数字字符串没有问题,放进数组后直接调用 map(parseInt),却出现了 NaN,甚至得到另一个数字:

const input = ["10", "10", "10"];
const values = input.map(parseInt);
// 预期为 [10, NaN, 2],不是 [10, 10, 10]

原因是两个函数对第二个参数的含义不同。本文针对标准 Array.prototype.map 与内置 parseInt,示例使用 ES2015 的语法;不涉及异步回调或数组排序。

第二个参数被当作进制

map 调用回调时会传入当前元素、索引和数组。parseInt 的第二个参数恰好是进制 radix,所以索引也参与了转换。

上面的三个元素依次相当于这样处理:

parseInt("10", 0); // 预期 10
parseInt("10", 1); // 预期 NaN
parseInt("10", 2); // 预期 2

进制为零时按输入判断,这里的 "10" 按十进制解析。进制为 1 不合法,结果是 NaN;二进制的 "10" 则表示十进制 2。因此,问题不只是某个元素转换失败,还可能悄悄产生合法但错误的数值。

判断时同时核对字符串和它在数组里的位置。如果同样的字符串换个位置就改变结果,应检查回调收到的附加参数,而不只是数据格式。

用包装函数固定十进制

若输入约定是十进制整数文本,明确只把当前值传给解析器,并固定进制:

const input = ["10", "10", "10"];
const values = input.map((value) => parseInt(value, 10));
console.log(values);
// 预期 [10, 10, 10]

包装函数会收到 map 的参数,但调用 parseInt 时由代码明确指定参数,不再让索引成为进制。输入若约定为二进制或其他进制,应填写相应值,不能一律改成 10。

不要写成 input.map(parseInt, 10)。map 的第二个参数是回调执行时使用的 thisArg,不会替换回调的第二个实参,因此它不能固定解析进制。

省略包装函数里的进制虽然能避开索引干扰,但仍会保留按输入识别进制的行为。例如以 0x 开头的文本会按十六进制处理。接口要求十进制时,应直接写出这个约定。

修复参数后,仍要确认解析规则

parseInt 允许只解析字符串开头的整数部分。parseInt("12px", 10) 的预期结果是 12,parseInt("3.9", 10) 的预期结果是 3;它们不会因为后面有其他字符就统一返回 NaN。

如果接口只允许完整的整数文本,需在转换前按接口格式检查整段字符串。不能把“没有得到 NaN”当作完整输入已经通过验证,也不能用它代替小数转换。超出普通 Number 精确整数范围的编号还应保持字符串,或单独设计大整数处理路径。

修复后,把同一个输入放在不同索引位置,确认结果一致;再检查零、负数、空字符串、带单位和带前缀的输入是否符合约定。保留原始文本用于比较,避免只检查结果是不是数字。

这项修改应集中在回调参数传递处。若错误数值已经写入存储,需要从原始字符串重新转换;仅回退代码无法从错误的数字推回原始文本。