高阶函数回调参数错位
把现成函数直接传给高阶函数时,回调的额外参数(index、array)可能被目标函数误接收,导致意想不到的结果。
[!info] related notes
高阶函数回调参数错位
一句话定义
把函数引用直接传给 map、filter 等高阶函数时,高阶函数传入的额外参数(index、array)会被目标函数接收,可能导致参数错位。
核心问题
map 的回调签名为:
callback(currentValue, index, array)
当你把一个现成函数直接传给 map 时:
array.map(fn)
实际执行的是:
fn(currentValue, index, array)
如果 fn 只需要第一个参数,多余的参数通常被忽略,没问题。
但如果 fn 有第二个参数且含义不同,就会出 bug。
经典案例 1:map(parseInt)
['1', '2', '3'].map(parseInt)
展开后的实际调用:
parseInt('1', 0) // 0 → 自动推断 → 1
parseInt('2', 1) // 1 → 非法进制 → NaN
parseInt('3', 2) // 2 → 二进制解析 '3'(无效) → NaN
结果:[1, NaN, NaN]
原因:map 传入的 index 被 parseInt 当成了 radix(进制参数)。
经典案例 2:map(Number) 反而没问题
['1', '2', '3'].map(Number)
展开后:
Number('1', 0) // 1(Number 忽略第二个参数)
Number('2', 1) // 2
Number('3', 2) // 3
结果:[1, 2, 3] ✓
因为 Number() 只用第一个参数,多余的参数无害。
经典案例 3:map(parseFloat)
['1.1', '2.2', '3.3'].map(parseFloat)
展开后:
parseFloat('1.1', 0) // 1.1
parseFloat('2.2', 1) // 2.2
parseFloat('3.3', 2) // 3.3
结果:[1.1, 2.2, 3.3] ✓
parseFloat 没有第二个参数,多余的被忽略。
经典案例 4:自定义函数
function add(a, b) {
return a + b;
}
[1, 2, 3].map(add)
展开后:
add(1, 0) // 1 + 0 = 1
add(2, 1) // 2 + 1 = 3
add(3, 2) // 3 + 2 = 5
结果:[1, 3, 5](不是预期的 [1, 2, 3])
判断方法
拿到一个函数引用传给高阶函数前,问自己:
- 目标函数的签名是什么? 有几个参数,每个参数含义是什么?
- 高阶函数会传什么?
map传(value, index, array),reduce传(acc, value, index, array) - 参数数量和含义是否对齐? 如果目标函数的第二个参数有特殊含义(如
parseInt的radix),就会出问题
解决方案
方案 1:箭头函数包装(推荐)
// 只传一个参数
['1', '2', '3'].map(x => parseInt(x, 10))
// 显式控制参数
[1, 2, 3].map((x, i) => add(x, 10))
方案 2:选择参数签名匹配的函数
// Number 只用第一个参数,安全
['1', '2', '3'].map(Number)
方案 3:bind 绑定固定参数
['1', '2', '3'].map(parseInt.bind(null, ???))
// 这种方式不太直观,一般不推荐
受影响的高阶函数
| 高阶函数 | 回调参数 | 需要警惕的场景 |
|---|---|---|
map(fn) | (value, index, array) | fn 的第二参数有含义时 |
filter(fn) | (value, index, array) | 同上 |
find(fn) | (value, index, array) | 同上 |
forEach(fn) | (value, index, array) | 同上 |
reduce(fn) | (acc, value, index, array) | fn 参数更多,更易错位 |
sort(fn) | (a, b) | 参数含义完全不同(两个待比较元素) |
面试回答模板
['1','2','3'].map(parseInt)的结果是[1, NaN, NaN]。原因是
map回调会传入(value, index, array)三个参数,而parseInt的签名是parseInt(string, radix)。所以index被误当成了进制参数。
parseInt('2', 1)中radix=1非法,返回NaN。正确写法是
arr.map(x => parseInt(x, 10))或arr.map(Number)。这道题本质考的是:高阶函数传回调时的参数透传机制,以及不要随手把函数引用直接传进去,除非参数签名完全匹配。