标准答案

  1. map 的核心是创建一个和原数组 length 相同的新数组,遍历存在的索引,把 callback(value, index, array) 的返回值写到对应位置。
  2. filter 的核心是遍历存在的索引,回调返回真值时,把当前元素 push 到结果数组。
  3. reduce 的核心是维护 accumulator。如果提供 initialValue,就从索引 0 开始;如果没有提供,就找到第一个存在的元素作为初始 accumulator。
  4. 空数组没有 initialValue 时,reduce 要抛 TypeError。手写时还要注意稀疏数组空洞、thisArg、回调参数顺序和不直接修改原数组。

题目解析

手写数组方法常见失分点不是主流程,而是边界:稀疏数组的空洞不应触发回调,reduce 没有初始值时要找第一个实际存在的元素。

map 返回等长数组,filter 返回长度不固定的新数组,reduce 返回累计结果。三者的返回值语义不同,不能混用。

真实规范还会处理 this 为 null/undefined、callback 是否函数、length 快照、类数组对象等细节。面试核心版至少要把回调参数、thisArg、空洞和 reduce 初始值说清。

代码示例

下面示例覆盖 map、filter、reduce 的核心逻辑和 reduce 初始值边界。

JavaScript
function myMap(array, callback, thisArg) {
  const result = new Array(array.length)

  for (let index = 0; index < array.length; index += 1) {
    if (index in array) {
      result[index] = callback.call(thisArg, array[index], index, array)
    }
  }

  return result
}

function myFilter(array, callback, thisArg) {
  const result = []

  for (let index = 0; index < array.length; index += 1) {
    if (index in array && callback.call(thisArg, array[index], index, array)) {
      result.push(array[index])
    }
  }

  return result
}

function myReduce(array, callback, initialValue) {
  let index = 0
  let accumulator = initialValue

  if (arguments.length < 3) {
    while (index < array.length && !(index in array)) {
      index += 1
    }

    if (index >= array.length) {
      throw new TypeError('Reduce of empty array with no initial value')
    }

    accumulator = array[index]
    index += 1
  }

  for (; index < array.length; index += 1) {
    if (index in array) {
      accumulator = callback(accumulator, array[index], index, array)
    }
  }

  return accumulator
}

常见误区

  • reduce 空数组无初始值时返回 undefined,而不是抛错。
  • map/filter 遍历稀疏数组空洞并调用回调。
  • 忘记 thisArg 和回调参数顺序。

作者信息