最强大的[JavaScript]函数

2025-06-04

最强大的[JavaScript]函数

Twitter 关注

你知道我有一份新闻通讯吗?📬

如果您想在我发布新博客文章或发布重大项目公告时收到通知
,请访问
https://cleancodestudio.paperform.co/


最强大的[JavaScript]函数


Today you learn how to use **THE MOST POWERFUL** JavaScript Function out there.


数组.reduce


Array.reduce最强大的 JavaScript Function. 时期。

  • 嗯——好吧,你懂我的意思。严格来说,这是我的观点。
  • 话虽如此,我完全相信这个观点。
  • 是的,看完这篇文章后,我希望你也能认同这个观点!

在本文结尾,我诚邀您在评论区挑战我的观点。我乐意参与一场互相矛盾的评论大战 :)

话虽如此,让我们开始吧!


文章结构和相关资源


文章结构

  • 简单的例子
    • 在文章开头
    • 并非旨在成为现实世界的用例
    • 旨在教如何简单地利用减少
    • 旨在通过用 reduce 替换函数来减少
    • 如果你已经了解如何实现 reduce,可以跳过
  • 中间示例
    • 遵循简单的例子
    • 旨在展示一些现实世界的用例
    • 并非旨在解释 reduce 最强大的部分
  • 高级示例
    • 中间示例后发现
    • 旨在展示现实世界的用例
    • 旨在解释更强大的现实世界减少用例
  • 专家很少谈论减少技巧/窍门
    • Array.reduce 的累加器回调参数
    • 如何打破 Array.reduce 的循环(类似于 break)
    • 如何改变 reduce 可以访问的原始源数组

相关资源:Array.Reduce YouTube 视频

  • 视频代码示例演示
  • YouTube 视频和这篇文章的内容非常相似
  • 注意:通过视频来理解一些例子会更简单


数组.reduce


Reduce 的作用是什么?它为什么这么强大?

好吧,这是 reduce 的技术定义......

Array.prototype.reduce()


reduce() 方法对数组的每个元素执行一个 reducer 函数(由您提供),从而生成单个输出值。

如果你问我,这没什么帮助,所以让我们边做边学


1. 计算所有数字的总和(使用 reduce)



[3, 2.1, 5, 8].reduce((total, number) => total + number, 0)

// loop 1: 0 + 3
// loop 2: 3 + 2.1
// loop 3: 5.1 + 5
// loop 4: 10.1 + 8
// returns 18.1

Enter fullscreen mode Exit fullscreen mode

2. 计算所有数字相乘的总和


[3, 2.1, 5, 8].reduce((total, number) => total * number, 1)
Enter fullscreen mode Exit fullscreen mode

如上所述,对数组中的所有数字进行加法或乘法运算非常容易。但是,拜托——我说过 Reduce 是所有 JS 函数中最强大的。

显然,还需要减少更多——对吗?

假设我们有3,,4假设我们想要获取其中一些值。1060

或者更具体地说,我们想要filter这些值,并且只返回小于 10 的值。

通常,我们可以简单地使用该filter函数,并且仅返回小于 10 的数字。


3. 使用 reduce 进行 Array.Filter


[3, 4, 10, 60].filter(number => number < 10)
Enter fullscreen mode Exit fullscreen mode

好吧,通过 reduce - 我们就可以做同样的事情。

[3, 4, 10, 60].reduce((list, number) => 
   number < 10
      ? [...list, number]
      : list
, [])
Enter fullscreen mode Exit fullscreen mode

哈哈,我们用 reduce 替换了 filter —— 很酷,但说实话,这仍然不足以reduce成为JavaScript 中最强大的函数


如果我告诉您,我们可以继续沿着这条路走下去,并使用 reduce 替换 JavaScript 中的几乎每个数组函数,您会怎么想?



Array.some3.使用重新创建Array.reduce


[3, 4, 10, 50].some(number => number < 50)

// returns true (We have some numbers in the array are less than 50)
Enter fullscreen mode Exit fullscreen mode

使用 reduce 时,我们只需将初始值设置为 false。如果条件已经成立,则返回条件。如果条件不成立,则检查当前数字是否满足条件。

[3, 4, 10, 50].reduce((condition, number) => 
   condition === true  
       ? condition
       : number < 50
, false)
Enter fullscreen mode Exit fullscreen mode

请注意,这次我们以 bool(false)作为初始值,而不是使用数字或数组。


现在我们已经求和、乘以、过滤并重新创建了一些(即使用 reduce 有条件地检查数组上的某些内容)。

我们也可以继续Array.every使用 来替换函数,但由于这与使用Array.reduce替换类似,我们只需注意我们也可以轻松地做到这一点。Array.someArray.reduce


Array.join4.使用怎么样Array.reduce


替换Array.joinArray.reduce

['truck', 'car', 'people'].join('-')

// "truck-car-people"
Enter fullscreen mode Exit fullscreen mode

我们可以编写Array.reduce以下代码

['truck', 'car', 'people'].reduce((text, word) => `${text}-${word}`, '')

// "-truck-car-people"
Enter fullscreen mode Exit fullscreen mode

请注意,输出前面有一个破折号。

作为函数第一个参数的回调函数Array.reduce可以接受更多参数。我们可以使用第三个接受的参数来跟踪我们index的 Reduce 函数

['truck', 'car', 'people'].reduce((text, word, index) => 
    index === 0
       ? word 
       : `${text}-${word}`
, '')

// "truck-car-people"
Enter fullscreen mode Exit fullscreen mode

设置好第三个参数后,这个 reduce 函数现在将按照原始函数的方式Array.join运行


至此,我们已经使用 reduce 来替代。

  • 数组.map
  • 数组.过滤器
  • 数组.every,数组.some
  • 数组.join

5. 使用 Reduce 进行 Array.concat


那么 concat 呢?你可以把一个包含 "1"、"2" 和 "3" 的数组和另一个数组连接起来吗?

[1, 2, 3].concat(['hey', 'world', 'mars'])

// [1, 2, 3, 'hey', 'world', 'mars']
Enter fullscreen mode Exit fullscreen mode

您将如何连接或组合数组以进行减少?

[[1,2,3], ['hey', 'world', 'mars']].reduce(
   (list, array) => [...list, ...array],
[])

// [1, 2, 3, 'hey, 'world', 'mars']
Enter fullscreen mode Exit fullscreen mode

使用组合数组的妙处Array.reduce在于,我们可以“连接”任意数量的数组。

简单来说,通过传入更多数组,我们将使用 reduce 自动组合或连接它们。

这样,我们复制了Array.concat使用Array.reduce


让我们再看几个例子。

首先,让我们创建几个人物。

let sarah = { name: 'sarah', email: 'sarah@gmail.com', id: 1 }
let tim = { name: 'tim', email: 'tim@gmail.com', id: 2 }
let len = { name: 'len', email: 'len@gmail.com', id: 3 }
Enter fullscreen mode Exit fullscreen mode

6. 使用以下方式按姓名对人员进行分组Array.reduce


当我们按姓名对人员进行分组时我们想要实现的目标示例

people.len

// Gets Len
// { name: 'len', email: 'len@gmail.com', id: 3 }


people.sarah

// Gets sarah
// { name: 'sarah', email: 'sarah@gmail.com', id: 1}
Enter fullscreen mode Exit fullscreen mode

使用 reduce 根据姓名对人员进行分组

  • 将 reduce 函数的初始值设为一个对象
  • 构建一个对象
    • 关键是人的名字([person.name])
    • 该值是 person 对象([person.name]: person)

示例(这不起作用)

let people = [sarah, tim, len].reduce((people, person) => {
   [person.name]: person,
   ...people
}, {}) 
Enter fullscreen mode Exit fullscreen mode

在上面的例子中,我们会得到一个错误

未捕获的语法错误:意外的标记“:”

每当我们使用简写函数返回一个对象时,我们都需要将其括在括号中

  • 将返回对象的括号括起来以修复错误
let people = [sarah, tim, len].reduce((people, person) => ({
   [person.name]: person,
   ...people
}), {}) 
Enter fullscreen mode Exit fullscreen mode

啦啦,我们现在有一个人员对象,其中的人员按其姓名分组

如果我们去的话,people.len我们会得到 len

people.len // { name: 'len', email: 'len@gmail.com', id: 3 }
Enter fullscreen mode Exit fullscreen mode

如果我们去的话people.sarah我们会得到莎拉

people.sarah // { name: 'sarah', email: 'sarah@gmail.com', id: 1 }
Enter fullscreen mode Exit fullscreen mode

如果我们去的话,people.tim我们会有时间

people.tim // { name: 'tim', email: 'tim@gmail.com', id: 2 }
Enter fullscreen mode Exit fullscreen mode

如果我们想要我们所有的people

// people 
{
   sarah: { name: 'sarah', email: 'sarah@gmail.com', id: 1 },
   tim: { name: 'tim', email: 'tim@gmail.com', id: 2 },
   len: { name: 'len', email: 'len@gmail.com', id: 3 },

}
Enter fullscreen mode Exit fullscreen mode

7. 使用 Reduce 根据给定键提取数组值


不仅如此,如果我们只想获得人们的姓名该怎么

let names = [sarah, tim, len].reduce((names, person) => [
   ...names,
   person.name
], [])

// ['sarah', 'tim', 'len']
Enter fullscreen mode Exit fullscreen mode

如果我们只想获取人们的电子邮件怎么办?

let emails = [sarah, tim, len].reduce((emails, person) => [
   ...emails,
   person.email
], [])

// ['sarah@gmail.com', 'tim@gmail.com', 'len@gmail.com']
Enter fullscreen mode Exit fullscreen mode

8. 使用 Reduce 展平多层嵌套数组


不仅如此,如果我们有一个嵌套数组的数组会怎么样?

let list_of_arrays = [
    ['sub_one', 'sub_two', 'sub_three'],
    [
       ['nested_sub_one', 'nested_sub_two'], 
       ['nested_sub_three', 'nested_sub_four']
    ],
    'one',
    'two',
    'three'
]
Enter fullscreen mode Exit fullscreen mode

让我们来看看数组列表,当然,我们使用 reduce

list_of_arrays.reduce((flattened, item) => {
   if (Array.isArray(item) === false) {
      return [...flattened, item]
   }
   if (Array.isArray(item) && Array.isArray(item[0])) {
      return [
         ...flattened,
         ....item.reduced((flatten, nested_list) => [...flatten, ...nested_list, [])
       ]
      ]
   }

   return [...flattened, ...item]
}, [])
Enter fullscreen mode Exit fullscreen mode

啦啦,我们已经展平了多层嵌套数组的列表。

输出

["sub_one", "sub_two", "sub_three", "nested_sub_one", "nested_sub_two", "nested_sub_three", "nested_sub_four", "one", "two", "three"]
Enter fullscreen mode Exit fullscreen mode

笔记:

我们仅处理深度不超过 3 级的嵌套子数组,但您当然可以在函数上花费更多时间,并使用递归来使用 reduce 非常简单地展平无限嵌套级深的数组。


Reduce 的更多强大用例


好的,现在让我们深入研究一些更强大但不常用的用例Array.reduce


9. 在字符串上应用格式化程序


我将从一个字符串数组开始。

let strings = ['cool-link', 'hello world of javascript', 'goodbye, its been swell']
Enter fullscreen mode Exit fullscreen mode

接下来,我们创建一个数组formatters。通常,我会称它们为过滤器——但它们实际上并不是过滤器。它们只是格式化字符串。

这些格式化程序实际上将成为回调函数。

首先,我们将创建一个将破折号替换为空格的格式化程序(将破折号替换为空格)。我们将使用正则表达式来实现这个格式化程序。

let dashesToSpaces = str => str.replace(/-/g, ' ')
Enter fullscreen mode Exit fullscreen mode

接下来,我们将创建一个大写字符串格式化程序。

let capitalize = str => `${str[0].toUpperCase()}${str.slice(1)}`
Enter fullscreen mode Exit fullscreen mode

然后,我们将创建一个字符串限制器格式化程序。

如果字符串大于给定的长度,则用三个点替换该长度限制后的字符。

let limiter = str => str.length > 10 ? `${str.slice(0, 10)}...` : str 
Enter fullscreen mode Exit fullscreen mode

最后,我们将创建一个formatters包含所有字符串格式化程序的数组。

let formatters = [dashesToSpaces, capitalize, limiter]
Enter fullscreen mode Exit fullscreen mode

记住我们有字符串数组。

let strings = ['cool-link', 'hello world of javascript', 'goodbye, its been swell']
Enter fullscreen mode Exit fullscreen mode

我们的目标:

我们的目标是将格式化程序数组中的每个格式化程序应用到字符串数组中的每个字符串上。

使用 reduce,我们可以简单地这样做!

strings.reduce((list, str) => [
      formatters.reduce((string, format) => format(string), str),
      ...list
   ],
[])
Enter fullscreen mode Exit fullscreen mode

_就这样,我们使用 reduce 将格式化程序数组应用于字符串数组。_

原始字符串数组

['cool-link', 'hello world of javascript', 'goodbye, its been swell']
Enter fullscreen mode Exit fullscreen mode

输出(使用 reduce 应用字符串格式化程序后)

["Goodbye, i...", "Hello worl...", "Cool link"]
Enter fullscreen mode Exit fullscreen mode

10. 按房间对学生进行分组(使用 reduce)


首先让我们创建一些学生

let students = [
   { name: 'Sally', room: 'A' },
   { name: 'tim', room: 'A' },
   { name: 'nick', room: 'B' },
   { name: 'rick', room: 'C' },
   { name: 'sarah', room: 'B' },
   { name: 'pam', room: 'C' }
]
Enter fullscreen mode Exit fullscreen mode

我们想根据学生的房间对他们进行分组

所以我们要做的是使用students.reduce

students.reduce((class_rooms, student) => ({
}), {})
Enter fullscreen mode Exit fullscreen mode

注意,我们再次使用括号将隐式返回的对象括起来。当我们使用简写函数返回对象时,必须使用({})语法——如果我们尝试直接返回一个不带括号的对象,()则会报错。

接下来,我们要使用学生房间作为钥匙:

students.reduce((rooms, student) => ({
   ...rooms,
   [student.room]: rooms[student.room]
        ? [...rooms[student.room], student]
        : [student]
}), {})
Enter fullscreen mode Exit fullscreen mode

现在,我们根据学生的房间/班级对他们进行分组。

{
   A: [{ name: 'sally', room: 'A' }, { name: 'tim', room: 'A' }],
   B: [{ name: 'nick', room: 'B' }, { name: 'sarah', room: 'B'}],
   C: [{ name: 'rick', room: 'C' }, { name: 'pam', room: 'C' }],
}
Enter fullscreen mode Exit fullscreen mode

我们已经成功地根据学生的房间对他们进行了分组 - 这就是我们按减少方式分组的方法。


好了,伙计们,这就是我对 reduce 的全部理解了。我想最大的收获是,reduce 是一个超级方法——真的!

使用 reduce,你可以完成使用其他Array 方法可以完成的任何操作。

Array.filter.map.filter.forEach您可以使用单个 reduce 函数来实现相同的目标

如果需要按键对一大堆对象进行分组,请使用 reduce。

如果需要提取与给定键相关的值?请使用 reduce。

如果您需要应用多个过滤器,但不想通过多次迭代同一个数组来增加时间复杂度 - 请使用 reduce。

如果你想展平一个嵌套数组,其中每个嵌套数组可能包含更多嵌套数组,而每个嵌套数组也可能没有任何嵌套数组?使用 reduce。

如果您需要对某些数字求和、对某些数字乘以、对某些数字减去或进行任何类型的算术运算 - reduce 可以再次发挥作用。

如果需要合并一些数组怎么办?使用 reduce。

如果需要合并一些对象怎么办?使用 reduce。

如果您想要一种可以完成所有事情的方法,并让您作为一名软件工程师感觉更强大、更高效,那该怎么办?

使用减少!

在我看来,forEach 是 JavaScript 生态系统中最被高估的方法,而 reduce 是JS 生态系统中最被低估的方法。


作为 reduce 有多酷的最后一个例子,让我们来看看这个最后的例子。

[{ name: 'Clean Code Studio' }, { belief: 'Simplify!' }, { should_follow: 'Si, senor!' }].reduce((last_example, partial) => ({ 
   ...last_example, ...partial }), {})

Enter fullscreen mode Exit fullscreen mode

返回什么?它合并了所有对象。

{

   name: 'Clean Code Studio',
   belief: 'Simplify',
   should_follow: 'Si, senor!'
}
Enter fullscreen mode Exit fullscreen mode

使用 reduce 您可以过滤、应用、应用回调列表、展平、合并、组合...

强烈建议您在使用 reduce 时变得熟悉、熟练并且全面熟悉。

因此,再次使用 reduce 你有两个参数。

  • 累加器-回调函数
  • 初始值——在累加器回调函数第一次迭代期间使用
[].reduce(accumulatorCallbackFunction, initialValue)
Enter fullscreen mode Exit fullscreen mode

Accumulator回调函数有四个参数

  • accumulator - 每次迭代后从回调函数返回的值
  • item - 数组中的元素
  • index - 传递到累加器回调中的当前元素的索引
  • 源 - 原始数组 reduce 被调用
let initial = []
let callback = (accumulator, item, index, source) => {}

[].reduce(callback, initial)
Enter fullscreen mode Exit fullscreen mode

最后,最后一个额外提示 - 如果您想在完成所有项目的迭代之前退出 reduce 怎么办?


[].reduce((build, item, index, source) => source.slice(index), 0)

Enter fullscreen mode Exit fullscreen mode

通过在给定索引处对源数组进行切片,您将摆脱 reduce 函数循环 - 因此如果您有一个大数据集,则一旦满足条件,您就可以停止使用计算资源。


最后,我强烈推荐大家练习一下 Reduce。我发现它是 JavaScript 函数中最实用的。很多时候,Reduce 都能以简洁明了的方式解决复杂的编码难题。

这里涵盖了我们讨论的每个概念的演示Array.reduce。请查看我制作的截屏视频 - 我们将深入讲解 Reduce。我们从简单的开始,逐步讲解,最终涵盖本文分享的所有示例。

感谢您的收看,如果您有任何意见、问题或疑虑,请在下方评论区留言:)

Clean Code Studio
Clean Code Clean Life
设计模式
算法
数据结构
重构
简化


(读完这篇文章后)仍然不同意我的观点吗?

  • 让我们讨论一下(或者辩论一下——由你决定)——在下面评论:)
    • 您认为 JS 是否具有最佳功能?
    • 您认为 JS 中是否有比 reduce 更好的函数?
    • 您在哪些方面不同意我的思维过程?


你知道我有一份新闻通讯吗?📬

如果您想在我发布新博客文章或发布重大项目公告时收到通知。

文章来源:https://dev.to/cleancodestudio/the-most-powerful-function-javascript-hej
PREV
创建二维码生成器
NEXT
疯狂的 Laravel Stack(非常强大)DB_HOST=127.0.0.1 DB_DATABASE=数据库