前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库空数组 reduce 无初始值错误
JavaScript数组与集合

为什么空数组 reduce 不传初始值会抛错,应该返回什么才合理?

reduce 不传初始值时,会拿数组第一个元素当累加器的起点。空数组连第一个元素都没有,只能抛 TypeError。修法是传初始值,但初始值要选对:求和用 0,收集结果用 [],求最大值不能随手填 0。

前端进阶之旅 · 一题精讲更新于 2026.09.29
JavaScript#数组与集合
先看核心答案读代码示例
理解线索

初始值决定空数组时返回什么

  1. 不传初始值第一个元素当累加器,从第二个元素开始调用回调
  2. 传初始值从第一个元素开始调用回调,空数组直接返回初始值
  3. 单元素数组不传初始值时回调一次都不执行,直接返回那个元素

单元素数组不传初始值时,回调根本不会执行。如果回调里有类型转换或校验逻辑,这时会被整个跳过。

核心回答

先记住这个答案

reduce 不传初始值时,会把数组的第一个元素当作累加器的起点,然后从第二个元素开始调用回调。空数组没有第一个元素,没法开始,所以抛出 TypeError: Reduce of empty array with no initial value。传了初始值之后,空数组会直接返回这个初始值,回调一次都不执行。初始值要符合业务含义:求和用 0,拼数组用 [],但求最大值这类场景不能填 0,要单独处理空数组。

  • 不传初始值时,第一个元素就是累加器起点
  • 空数组加无初始值,直接抛 TypeError
  • 传了初始值,空数组直接返回初始值
  • 求最大值填 0 会让全负数的结果出错

最常见的触发场景:filter 之后再 reduce

接口返回的数组明明有数据,页面却偶尔报错,通常是因为中间有一步 filter。某些筛选条件下一项都没剩,后面的 reduce 拿到的就是空数组。

过滤后变成空数组JavaScript
const selected = [-2, 0].filter(value => value > 0)   // 没有正数,得到 []

try {
  selected.reduce((sum, value) => sum + value)        // 不传初始值
} catch (error) {
  console.log(error.name)
}

let calls = 0
const total = selected.reduce((sum, value) => { calls++; return sum + value }, 0)
console.log(total, calls)        // 直接返回初始值 0,回调没执行

console.log([9].reduce((acc, value) => acc + value))  // 单元素:直接返回 9
查看输出与解释
TypeError
0 0
9

不传初始值时抛了 TypeError。传了 0 之后,空数组直接返回 0,calls 也是 0,说明这个 0 不是算出来的,是初始值原样返回。最后一行是单元素数组不传初始值的情况:元素 9 被拿来当累加器,没有第二个元素要合并,回调一次都没调用,直接返回 9。 这也提醒我们:想在回调里做校验或格式转换,不传初始值时第一个元素永远不会经过回调。

初始值不能一律填 0

补一个 0 能消掉报错,但不一定对。初始值应该是「一个元素都没有时,这个结果该是什么」。

按运算选初始值
求和用 0,求积用 1,拼数组用 [],拼对象用 {},这些都是加进去不改变结果的值。求最大值就不一样了:数组是 [-5, -3] 时,初始值填 0 会得到 0,而数组里根本没有 0。 求最大值的正确做法是先判断空数组:arr.length ? Math.max(...arr) : null,或者用 -Infinity 当初始值。如果结果要展示或者转 JSON,-Infinity 转出来是 null,还不如直接返回 null 让调用方明确处理「没有数据」这种情况。 用对象或数组做初始值时,要在每次调用时新建,不要把一个共享的对象传进去,否则多次聚合会互相污染。
回答前,多想一步

容易答错的地方

用 try...catch 捕获错误然后返回 0
这会把回调里真正的程序错误也一起吞掉。空数组是可以预见的情况,应该用初始值或者提前判断,而不是靠捕获异常兜底。
以为 length 大于 0 就一定不会报错
稀疏数组 new Array(3) 的 length 是 3,但里面一个元素都没有,reduce 会跳过空位,照样找不到起点,同样抛 TypeError。
试着用自己的话回答

面试官还会怎么问?

显式传 undefined 当初始值算传了吗?

算。reduce 判断的是有没有传第二个参数,不是它的值。[].reduce(fn, undefined) 返回 undefined,不报错。但非空数组的第一次回调里累加器就是 undefined,求和会得到 NaN。

用 TypeScript 写 reduce 要注意什么?

不传初始值时,累加器类型只能和元素类型一样。想把数组聚合成另一种类型,比如把字符串数组变成对象,就必须传初始值,并通过泛型写明类型:arr.reduce<Record<string, number>>((acc, key) => acc, {})。

reduceRight 的规则一样吗?

一样。只是方向反过来,从最后一个元素开始当累加器。空数组不传初始值同样抛 TypeError。

从一道题,走向一组知识

把知识连起来

数组与集合

reduce 和 reduceRight 在顺序敏感的计算里有什么区别?

reduceRight 找初始累加器的规则一样,只是从数组末尾开始,空数组同样会抛错。

数组与集合

稀疏数组的空位和 undefined 有什么区别,为什么 Array(n).map 不执行?

长度不为 0 但全是空位的数组,reduce 同样找不到起点,那篇讲空位是怎么回事。

参考资料

  • MDN:reduce

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 最常见的触发场景:filter 之后再 reduce
  3. 初始值不能一律填 0
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑