前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
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 与 reduceRight 遍历方向
JavaScript数组与集合

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

reduce 从下标 0 往后处理,reduceRight 从最后一个下标往前处理,两者的回调参数顺序一样,都是 (累加器, 当前值)。加法、乘法这类和顺序无关的运算结果相同;字符串拼接、减法、函数组合的结果就不一样了。

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

方向不同带来的三点差异

  1. 遍历顺序reduce 从下标 0 开始,reduceRight 从 length - 1 开始
  2. 没有初始值时的起点reduce 拿第一个元素当累加器,reduceRight 拿最后一个
  3. 回调里的下标reduceRight 传给回调的仍是元素在原数组里的下标,从大到小

空数组不传初始值时,两个方法都会抛 TypeError。只要数组可能为空,就一定要给初始值。

核心回答

先记住这个答案

reduce 从最小的下标向最大的下标处理,reduceRight 正好相反,从最后一项往前处理。但两者回调的参数顺序是一样的,第一个参数都是累加器,第二个才是当前元素。有初始值时,初始值就是第一步的累加器;没有初始值时,reduce 用第一个元素当起点,reduceRight 用最后一个元素当起点。对于加法、乘法这类交换顺序不影响结果的运算,两者结果相同;字符串拼接、减法、函数组合这类和顺序有关的运算,结果就不同了。拿不准时,把前两三步手动展开写出来,一看就知道。

  • reduceRight 只是遍历方向相反,参数顺序不变
  • 可交换的运算两者结果相同,减法、拼接会不同
  • compose 用 reduceRight,而 pipe 用 reduce
  • reverse().reduce() 会改掉原数组,reduceRight 不会

顺序敏感的运算结果不同

用拼接和减法把两种方向的每一步展开看。

字符串拼接和减法JavaScript
const arr = ['a', 'b', 'c']
console.log(arr.reduce((acc, x) => acc + x)) // ('a' + 'b') + 'c'
console.log(arr.reduceRight((acc, x) => acc + x)) // ('c' + 'b') + 'a'

console.log([10, 3, 2].reduce((acc, x) => acc - x)) // (10 - 3) - 2
console.log([10, 3, 2].reduceRight((acc, x) => acc - x)) // (2 - 3) - 10
查看输出与解释
abc
cba
5
-11

减法的例子最能说明问题。reduce 没有初始值,拿 10 当累加器,先减 3 再减 2,得到 5。reduceRight 拿最后一项 2 当累加器,先减 3 得到 -1,再减 10 得到 -11。两个结果相差很大,原因就是减法不满足交换律。 加法没有这个问题,[1, 2, 3] 不管从哪边加都是 6。所以求和、计数、取最大值用哪个都行,习惯上用 reduce。

最典型的用途:函数组合

函数组合 compose(f, g)(x) 的含义是 f(g(x)),也就是先执行最右边的函数,结果再交给左边的。这个「从右往左」正好对应 reduceRight。

compose 和 pipeJavaScript
const compose = (...fns) => (x) => fns.reduceRight((acc, fn) => fn(acc), x)
const pipe = (...fns) => (x) => fns.reduce((acc, fn) => fn(acc), x)

const add1 = (x) => x + 1
const double = (x) => x * 2

console.log(compose(add1, double)(5)) // add1(double(5))
console.log(pipe(add1, double)(5)) // double(add1(5))
查看输出与解释
11
12

compose 的写法和数学上的函数组合一致,Redux 的 compose 就是这个语义,中间件从右往左包装。pipe 按书写顺序从左往右执行,读起来更像流水线,很多函数式工具库两个都提供。面试手写 compose 时,用 reduceRight 是最直接的写法。

为什么不写成 reverse().reduce()
结果看起来一样,但有两个差别。reverse 会原地修改数组,fns.reverse() 之后传进来的函数数组顺序就被改掉了,下次调用会出错。另外回调第三个参数的下标也不同:reduceRight 给的是元素在原数组里的下标,reverse 之后给的是新顺序里的下标。
回答前,多想一步

容易答错的地方

以为 reduceRight 的回调参数也反过来了
参数顺序永远是 (累加器, 当前值, 下标, 数组),而 reduceRight 只改变遍历方向。把回调写成 (x, acc) => ... 会把两者弄反,减法、拼接的结果就错了。
以为 reduceRight 就是函数式语言里的右折叠
Haskell 的 foldr 对 [10, 3, 2] 做减法,计算的是 10 - (3 - 2),结果是 9;JavaScript 的 reduceRight 计算的是 (2 - 3) - 10,结果是 -11。原因是 reduceRight 的累加器还是放在左边。说「reduceRight 等于右折叠」要加上这个前提,面试官追问时才不会被问住。
空数组不传初始值
[].reduceRight((a, b) => a + b) 会抛 TypeError: Reduce of empty array with no initial value。只有一个元素且没有初始值时,回调不会被调用,直接返回那个元素。
试着用自己的话回答

面试官还会怎么问?

稀疏数组上 reduceRight 怎么处理空位?

和 reduce 一样跳过空位,回调不会在空位上被调用。没有初始值时,它会从末尾往前找到第一个真实存在的元素作为起点,而不是直接拿 length - 1 上的值。

手写一个 reduceRight?

从 length - 1 循环到 0,用 i in arr 跳过空位。没有初始值时,先往前找到第一个存在的元素作为累加器,找不到就抛 TypeError;然后对剩下的每一项调用 callback(acc, arr[i], i, arr),最后返回累加器。

函数组合里如果有异步函数,怎么写?

让累加器始终是 Promise:fns.reduceRight((p, fn) => p.then(fn), Promise.resolve(x))。每个函数等上一个的结果兑现后才执行,顺序仍然是从右往左。

从一道题,走向一组知识

把知识连起来

数组与集合

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

reduceRight 和 reduce 一样,空数组不给初始值就抛错,那题讲了原因和写法。

数组与集合

for...of 和 forEach 怎么跳过当前项、提前结束,异步时有什么区别?

reduce 系列不能中途退出,需要提前结束或者逐项 await 时要换成 for...of。

参考资料

  • MDN:reduceRight
  • MDN:reduce

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

本题目录
  1. 先记住这个答案
  2. 顺序敏感的运算结果不同
  3. 最典型的用途:函数组合
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

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

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