map保持索引结构并跳过空位但保留位置,filter按布尔转换筛选且不跳过空位,reduce依赖初始值决定累加器起始状态:无初值时空数组报错、单元素直接返回、多元素时accumulator从首项开始。

JavaScript 中的 map、filter、reduce 并非语法糖,而是有明确执行契约的原生方法。它们的底层实现围绕「遍历 + 回调执行 + 结果组装」三步展开,但各自对边界条件、稀疏数组、this 绑定和错误处理有严格约定。
map 的核心逻辑:保持索引结构,逐项映射
原生 map 不只是简单 for 循环,它必须:
- 按索引顺序遍历(0 → length−1),且跳过空位(sparse array),但新数组对应位置仍保留空位
- 对每个存在的索引,调用
callback.call(thisArg, item, index, array),确保 this 正确绑定 - 返回新数组长度严格等于原数组
length,不能用push动态增长(否则会丢失空位) - 不修改原数组,也不触发 getter/setter(即纯函数式行为)
手动模拟时需用 i in array 判断索引是否存在,而非仅靠 array[i] !== undefined(因 0、false 等 falsy 值是合法元素)。
filter 的关键细节:布尔转换驱动筛选,不跳过空位
filter 的判定依据是回调返回值的显式布尔转换(Boolean(result)),不是字面量 true:
立即学习“Java免费学习笔记(深入)”;
- 对每个索引(包括空位),只要
i in array为真,就执行回调 -
0、''、null、NaN等 falsy 值不会被自动过滤——只有回调返回false、0、""等经Boolean()转为false时才跳过 - 返回数组长度由实际保留项数决定,与原数组无关
- 空数组输入直接返回空数组,不执行回调
reduce 的初始值机制与偏移规则
reduce 最易出错的是 accumulator 的起始状态:
- 提供初始值(如
arr.reduce(cb, init)):第一次调用cb(init, arr[0], 0, arr) - 未提供初始值(
arr.reduce(cb)):
▸ 若数组为空 → 抛TypeError
▸ 若数组仅一项 → 直接返回该项(不调用回调)
▸ 若数组 ≥2 项 → 第一次调用cb(arr[0], arr[1], 1, arr),accumulator 从索引 0 开始 - 回调必须有返回值,该返回值成为下一轮的 accumulator
这个“无初始值时 accumulator 起始为首个元素、currentValue 起始为第二个元素”的偏移逻辑,是手写 reduce 时最常颠倒的部分。
这些方法的底层都基于 C++ 引擎实现(如 V8 的内置函数),但语义契约完全暴露给 JS 层——理解这些规则,才能写出健壮的替代逻辑或正确调试链式调用中的意外行为。


















