Get to know MDN better
此页面由社区从英文翻译而来。了解更多并加入 MDN Web Docs 社区。
reduce() 方法对数组中的每个元素按序执行一个提供的 reducer 函数,每一次运行 reducer 会将先前元素的计算结果作为参数传入,最后将其结果汇总为单个返回值。
第一次执行回调函数时,不存在“上一次的计算结果”。如果需要回调函数从数组索引为 0 的元素开始执行,则需要传递初始值。否则,数组索引为 0 的元素将被用作初始值,迭代器将从第二个元素开始执行(即从索引为 1 而不是 0 的位置开始)。
下面的例子能够帮助你理解 reduce() 的用处——计算数组所有元素的总和:
reducer 逐个遍历数组元素,每一步都将当前元素的值与前一步的结果相加(该结果是之前所有步骤结果的总和)——直到没有更多需要相加的元素。
为数组中每个元素执行的函数。其返回值将作为下一次调用 callbackFn 时的 accumulator 参数。对于最后一次调用,返回值将作为 reduce() 的返回值。该函数被调用时将传入以下参数:
accumulator上一次调用 callbackFn 的结果。在第一次调用时,如果指定了 initialValue 则为指定的值,否则为 array[0] 的值。
currentValue当前元素的值。在第一次调用时,如果指定了 initialValue,则为 array[0] 的值,否则为 array[1]。
currentIndexcurrentValue 在数组中的索引位置。在第一次调用时,如果指定了 initialValue 则为 0,否则为 1。
array调用了 reduce() 的数组本身。
initialValue 可选第一次调用回调时初始化 accumulator 的值。如果指定了 initialValue,则 callbackFn 从数组中的第一个值作为 currentValue 开始执行。如果没有指定 initialValue,则 accumulator 初始化为数组中的第一个值,并且 callbackFn 从数组中的第二个值作为 currentValue 开始执行。在这种情况下,如果数组为空(没有第一个值可以作为 accumulator 返回),则会抛出错误。
使用“reducer”回调函数遍历整个数组后的结果。
如果数组为空且未提供 initialValue,则会抛出异常。
reduce() 方法是一个迭代方法。它按升序对数组中的所有元素运行一个“reducer”回调函数,并将它们累积到一个单一的值中。每次调用时,callbackFn 的返回值都作为 accumulator 参数传递到下一次调用中。accumulator 的最终值(也就是在数组的最后一次迭代中从 callbackFn 返回的值)将作为 reduce() 的返回值。
callbackFn 仅对已分配值的数组索引进行调用。不会对稀疏数组中的空槽进行调用。
与其他迭代方法不同,reduce() 不接受 thisArg 参数。callbackFn 调用时始终以 undefined 作为 this 的值,如果 callbackFn 未处于严格模式,则该值将被替换为 globalThis。
reduce() 是函数式编程中的一个核心概念,在函数式编程中,不可能改变任何值,因此为了累积数组中的所有值,必须在每次迭代中返回一个新的累加器。这种约定也适用于 JavaScript 的 reduce():应该在可能的情况下使用展开语法或其他复制方法来创建新的数组和对象作为累加器,而不是改变现有的累加器。如果你决定改变累加器而不是复制它,请记得仍然在回调中返回修改后的对象,否则下一次迭代将收到 undefined。
reduce() 不会改变被调用的数组,但是作为 callbackFn 提供的函数可能会改变数组。但需要注意的是,在第一次调用 callbackFn 之前,数组的长度会被保存。因此:
警告:上述类型的并发修改经常导致难以理解的代码,通常应避免(特殊情况除外)。
reduce() 方法是通用的。它只期望 this 值具有 length 属性和整数键属性。
像 reduce() 这样的递归函数可能非常强大,但有时可能很难理解,特别是对于缺乏经验的 JavaScript 开发人员。如果使用其他数组方法可以使代码更清晰,则开发人员必须权衡代码可读性与使用 reduce() 带来的好处。如果 reduce() 确实是最佳选择,应该通过良好的文档和语义化的变量命名来提高代码的可读性。
如果数组只有一个元素(无论位置如何)且未提供 initialValue,或者提供了 initialValue 但数组为空,则将返回该单个值,而不调用 callbackFn。
如果提供了 initialValue 且数组不为空,则 reduce 方法将始终从索引 0 开始调用回调函数。
如果未提供 initialValue,则对于长度大于 1、等于 1 和 0 的数组,reduce 方法将有不同的表现,如以下示例所示:
假如运行以下无初始值的 reduce() 代码:
回调函数会被调用四次,每次调用的参数和返回值如下表:
| 第一次调用 | 15 | 16 | 1 | 31 |
| 第二次调用 | 31 | 17 | 2 | 48 |
| 第三次调用 | 48 | 18 | 3 | 66 |
| 第四次调用 | 66 | 19 | 4 | 85 |
array 参数在整个过程中始终不会改变——它始终是 [15, 16, 17, 18, 19]。reduce() 返回的值将是最后一次回调返回值(85)。
在这里,我们以相同的算法 reduce 同一个数组,但提供 10 作为 initialValue:
回调函数会被调用五次,每次调用的参数和返回值如下表:
| 第一次调用 | 10 | 15 | 0 | 25 |
| 第二次调用 | 25 | 16 | 1 | 41 |
| 第三次调用 | 41 | 17 | 2 | 58 |
| 第四次调用 | 58 | 18 | 3 | 76 |
| 第五次调用 | 76 | 19 | 4 | 95 |
这种情况下 reduce() 返回的值是 95。
为了对包含在对象数组中的值进行求和,必须提供一个 initialValue,以便每个项都通过回调函数处理。
备注:可以使用 Set 和 Array.from() 来实现相同的效果,如 const arrayWithNoDuplicates = Array.from(new Set(myArray)),并且性能更好。
使用 filter() 和 map() 会遍历数组两次,但是你可以使用 reduce() 只遍历一次并实现相同的效果,从而更高效。(如果你喜欢使用 for 循环,你可以在遍历一次时使用 forEach() 进行过滤和映射。)
reduce() 会跳过稀疏数组中缺失的元素,但不会跳过 undefined 值。
reduce() 方法读取 this 的 length 属性,然后访问每个整数索引。
| ECMAScript® 2027 Language Specification # sec-array.prototype.reduce |
启用 JavaScript 以查看此浏览器兼容性表。