3 个简单代码片段助你理解数组 .reduce()!🥳
如果您觉得这篇文章有用,可以关注我的Twitter 账号,订阅我的邮件列表,或者查看我博客上的其他文章。我还有几个正在进行的业余项目,您可能想看看:
- ippy.io - 一款用于创建精美简历的应用程序
- many.tools - 为设计师和开发人员提供的实用工具集合
您是否对 JS Array 方法感到困惑.reduce()
?
如果是这样,别担心——你并不孤单。无论出于什么原因,对于许多开发人员来说,reduce 方法似乎需要一段时间才能理解。我自己也遇到过这种情况。幸运的是,一旦你分解开来,它并不太复杂。
我写这篇文章的目的是:
- 提供我认为有用的思考心理模型
.reduce()
。 - 提供一系列使用示例来加强您的理解
让我们开始吧。
心智模型
Reduce 经常被用来将数组缩减为单个值。虽然它确实能做到这一点,但我从未发现这对理解它的工作原理或我能用它做什么有什么特别的帮助。
对我来说,最简单的理解方式.reduce()
是将其视为一个奇特的例子.forEach()
。其基本原理是相似的:
- 我们提供了一个回调函数
- 我们遍历数组,并为每个数组项执行一次回调函数。
重要的区别在于.reduce()
:
- 每次调用回调时都会知道前一次调用的返回值(或者在第一次调用时,我们提供的初始值)。
- 一旦数组中不再有项目,则最后一次回调调用的返回值将作为
.reduce()
调用的最终结果返回。
事实上,我们可以自己使用一个精简的包装器来实现一个 reduce 函数.forEach()
:
const reduce = (array, callback, initialValue) => {
// Originally stores the initialValue then updated
// on each iteration with the return value of the callback.
let previousReturnValue = initialValue
// Iterate over the array items, updating the currentReturn value at each step.
array.forEach((item, index) => {
const result = callback(previousReturnValue, item, index, array)
previousReturnValue = result;
})
// Return the final value once all array items have been iterated over
return previousReturnValue
}
现在让我们看一些使用的示例.reduce()
,并逐步执行执行路径。
1. 添加数字数组
在这个常见的例子中,我们将一个数字数组相加以得出最终的总和:
const initialValue = 0
const numbersToAdd = [ 1, 2, 3, 4 ]
const addFunction = (runningTotal, numberToAdd) => {
return runningTotal + numberToAdd;
}
const sum = numbersToAdd.reduce(addFunction, initialValue)
console.log(sum)
// => 10
// =======================================================
// How was this calculated? Lets step through it:
// The addFunction callback is invoked for each array item:
// -- FIRST CALLBACK INVOCATION --
// Array Item => 1
// Previous return value => first invocation (so the initialValue is used)
// Callback invocation => numbersToAdd(0, 1)
// Callback return value => 1
// -- SECOND CALLBACK INVOCATION --
// Array Item => 2
// Previous return value => 1
// Callback invocation => numbersToAdd(1, 2)
// Callback return value => 3
// -- THIRD CALLBACK INVOCATION --
// Array Item => 3
// Previous return value => 3
// Callback invocation => numbersToAdd(3, 3)
// Callback return value => 6
// -- FOURTH (AND LAST) CALLBACK INVOCATION --
// Array Item => 4
// Previous return value => 6
// Callback invocation => numbersToAdd(6, 4)
// Callback return value => 10
// Final Result: 10
2. 找出数组中最大的一个
这里我们将使用 reduce 来查找数组中的最大值。
这是一个稍微特殊的情况,因为我们没有提供.reduce()
初始值参数。因此,.reduce()
跳过第一个数组项的回调,而是将其用作第二个数组项回调的初始值。
const numbers = [ 10, 40, 4, 50, 101 ]
const findLarger = (currentMax, numberToCheck) => {
if (numberToCheck > currentMax) {
return numberToCheck;
}
return currentMax;
}
const largest = numbers.reduce(findLarger)
console.log(largest)
// => 101
// =======================================================
// How was this calculated? Lets step through it:
// The findLarger callback is invoked for each array item:
// -- FIRST CALLBACK INVOCATION --
// Array Item => 10
// Previous return value => first invocation, and no initialValue provided
// Callback invocation => Not Invoked
// Callback return value => N/A
// -- SECOND CALLBACK INVOCATION --
// Array Item => 40
// Previous return value => nothing, however first array item will be used as the initialValue
// Callback invocation => findLarger(10, 40)
// Callback return value => 40
// -- THIRD CALLBACK INVOCATION --
// Array Item => 4
// Previous return value => 40
// Callback invocation => findLarger(40, 4)
// Callback return value => 40
// -- FOURTH CALLBACK INVOCATION --
// Array Item => 50
// Previous return value => 40
// Callback invocation => findLarger(40, 50)
// Callback return value => 50
// -- FIFTH (AND LAST) CALLBACK INVOCATION --
// Array Item => 101
// Previous return value => 50
// Callback invocation => findLarger(50, 101)
// Callback return value => 101
// Final Result: 101
3. 将数组分类为偶数/奇数。
当然,回调函数返回的值并不局限于数字.reduce()
。你可以返回任何你想要的值。在本例中,我们返回一个对象,该对象将数组值按奇数/偶数进行分类。
const initialValue = { even: [], odd: [] }
const numbersToCategorise = [1, 3, 4, 8, 10]
const categorisingReducer = (categories, numberToCategorise) => {
const isEven = numberToCategorise % 2 === 0
if (isEven) {
categories.even.push(numberToCategorise)
} else {
categories.odd.push(numberToCategorise)
}
return categories
}
const categories = numbersToCategorise.reduce(categorisingReducer, initialValue)
console.log(categories)
// => { even: [4, 8, 10], odd: [1, 3] }
// =======================================================
// How was this calculated? Again, lets step through it:
// The categorisingReducer callback is invoked for each array item:
// -- FIRST CALLBACK INVOCATION --
// Array Item => 1
// Previous return value => first invocation (so the initialValue is used)
// Callback invocation => categorisingReducer({ even: [], odd: [] }, 1)
// Callback return value => { even: [], odd: [1] }
// -- SECOND CALLBACK INVOCATION --
// Array Item => 3
// Previous return value => { even: [], odd: [1] }
// Callback invocation => categorisingReducer({ even: [], odd: [1] }, 3)
// Callback return value => { even: [], odd: [1, 3] }
// -- THIRD CALLBACK INVOCATION --
// Array Item => 4
// Previous return value => { even: [], odd: [1, 3] }
// Callback invocation => categorisingReducer({ even: [], odd: [1, 3] }, 4)
// Callback return value => { even: [4], odd: [1, 3] }
// -- FOURTH CALLBACK INVOCATION --
// Array Item => 8
// Previous return value => { even: [4], odd: [1, 3] }
// Callback invocation => categorisingReducer({ even: [4], odd: [1, 3] }, 8)
// Callback return value => { even: [4, 8], odd: [1, 3] }
// -- FIFTH (AND LAST) CALLBACK INVOCATION --
// Array Item => 10
// Previous return value => { even: [4, 8], odd: [1, 3] }
// Callback invocation => categorisingReducer({ even: [4, 8], odd: [1, 3] }, 10)
// Callback return value => { even: [4, 8, 10], odd: [1, 3] }
// Final Result: { even: [4, 8, 10], odd: [1, 3] }
.reduce()
如果这有用,或者您对该方法还有什么不清楚的地方,请在评论中告诉我!