标准答案

  1. reduce 接收 callback 和可选 initialValue。
  2. callback 接收 accumulator、currentValue、index 和原数组。
  3. 传入 initialValue 时,accumulator 初始为该值,并从第一个元素开始遍历。
  4. 未传 initialValue 时,要找到第一个存在的数组元素作为初始 accumulator。
  5. 空数组且未传 initialValue 时应该抛出 TypeError。

题目解析

reduce 最容易写错的是初始值。很多简化实现默认拿 array[0],但稀疏数组可能第 0 位不存在,空数组也需要抛错。

reduce 是很多数组方法的底层表达能力来源,可以用来做求和、分组、索引映射和管道组合。但生产代码里不要为了炫技把简单逻辑都写成难读的 reduce。

实现时还要区分“有没有传 initialValue”,不能用 initialValue 是否为 truthy 判断,因为 0、false、空字符串都可能是合法初始值。

代码示例

下面的实现用 arguments.length 判断是否传入初始值。

JavaScript
function reduce(array, callback, initialValue) {
  if (array == null) {
    throw new TypeError('array is null or undefined')
  }

  if (typeof callback !== 'function') {
    throw new TypeError('callback must be a function')
  }

  const source = Object(array)
  const length = source.length >>> 0
  let index = 0
  let accumulator

  if (arguments.length >= 3) {
    accumulator = initialValue
  } else {
    while (index < length && !(index in source)) {
      index += 1
    }

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

    accumulator = source[index]
    index += 1
  }

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

  return accumulator
}

常见误区

  • 用 initialValue || array[0] 判断初始值,导致 0、false、空字符串被误判。
  • 空数组未传初始值时没有抛错。
  • 没有跳过稀疏数组不存在的索引。

作者信息