资讯专栏INFORMATION COLUMN

深入理解全能的 Reducer

zhongmeizhi / 2610人阅读

摘要:接下来,我们可以创建一个函数,它接受一个累加器和一个项。累加器是在上一次调用中返回的累积值或者是,是下一个回调的输入值。在中,我们首先判断这个是否存在于累加器中,如果是存在,加。

翻译: 刘小夕

原文链接:https://css-tricks.com/unders...

更多文章可戳: https://github.com/YvetteLau/...

有一些小伙伴,对JavaScript的 reduce 方法还不够理解,我们来看下面两段代码:

const nums = [1, 2, 3];
let value = 0;

for (let i = 0; i < nums.length; i++) {
    value += nums[i];
}
const nums = [1, 2, 3];
const value = nums.reduce((ac, next) => ac + next, 0);

这两段代码在功能上是等价的,都是数组中所有数字的总和,但是它们之间有一些理念差异。让我们先研究一下 reducer,因为它们功能强大,而且在编程中很重要。有成百上千篇关于 reducer 的文章,最后我会链接我喜欢的文章。

reducer 是什么

要理解 reducer 的第一点也是最重要的一点是它永远返回一个值,这个值可以是数字、字符串、数组或对象,但它始终只能是一个。reducer 对于很多场景都很适用,但是它们对于将一种逻辑应用到一组值中并最终得到一个单一结果的情况特别适用。

另外需要说明:reducer 本质上不会改变你的初始值;相反,它们会返回一些其他的东西。

让我们回顾一下第一个例子,这样你就可以看到这里发生了什么,一起看一下下面的gif:

观看gif也许对我们所有帮助,不过还是要回归代码:

const nums = [1, 2, 3];
let valu
![clipboard.png](/img/bVbv3AR)
let i = 0; i < nums.length; i++) {
    value += nums[i];
}

数组 nums ([1,2,3]) ,数组中的每个数字的第一个值将被添加到 value (0)。我们遍历数组并将其每一项添加到 value

让我们尝试一下不同的方法来实现此功能:

const nums = [1, 2, 3];
const initialValue = 0;

const reducer = function (acc, item) { 
    return acc + item;
}

const total = nums.reduce(reducer, initialValue);

现在我们有了相同的数组,但这次我们不会改变初始值(即前段代码中的 value)。这里,我们有一个仅在开始时使用的初始值。接下来,我们可以创建一个函数,它接受一个累加器(acc)和一个项(item)。累加器是在上一次调用中返回的累积值(或者是 initialValue),是下一个回调的输入值。在这个例子中,你可以把它想象成一个滚下一座山的雪球,当它以每一个吃过的值的大小增长时,它会吃掉它路径中的每个值。

我们将使用 .reduce() 来接收这个函数并从初始值开始。可以使用箭头函数简写:

const nums = [1, 2, 3];
const initialValue = 0;

const reducer = (acc, item) => { 
    return acc + item;
}

const total = nums.reduce(reducer, initialValue);

进一步缩短代码长度,我们知道箭头函数,在没有 {} 时,默认 return;

const nums = [1, 2, 3];
const initialValue = 0;

const reducer = (acc, item) => acc + item;

const total = nums.reduce(reducer, initialValue);

现在我们可以在调用它的地方应用这个函数,也可以直接设置初始值,如下:

const nums = [1, 2, 3];

const total = nums.reduce((acc, item) => acc + item, 0);

累加器可能是一个令人生畏的术语,所以当我们在回调调用上应用逻辑时,你可以将它想象成数组的当前状态。

调用栈

如果不清楚发生了什么,让我们记录下每次迭代的情况。reduce 使用的回调函数将针对数组中的每个项运行。下面的演示将有助于更清楚地说明这一点。我使用了一个不同的数组([1,3,6]),因为数字与索引相同可能会令人困惑。

const nums = [1, 3, 6];

const reducer4 = function (acc, item) { 
    console.log(`Acc: ${acc}, Item: ${item}, Return value: ${acc + item}`);
    return acc + item;
}
const total4 = nums.reduce(reducer4, 0);

当我们执行这段代码时,我们会在控制台看到以下输出:

Acc: 0, Item: 1, Return value: 1
Acc: 1, Item: 3, Return value: 4
Acc: 4, Item: 6, Return value: 10

下面是一个更直观的分解:

累加器(acc)从初始值(initialValue):0 开始的

然后第一个 item是1,所以返回值是1(0+1=1)

1在下次调用时成为累加器

现在我们累加器是1(acc),item (数组的第二项)是3

返回值变为4(1+3=4)

4在下次调用时成为累加器,调用时的下一项 item 是6

结果是10(4+6=10),是我们的最终值,因为6是数组中的最后一项

简单示例

既然我们已经掌握了这一点,那么让我们来看看 reducer 可以做的一些常见和有用的事情。

我们有多少个X?

假设您有一个数字数组,并且希望返回一个报告这些数字在数组中出现的次数的对象。请注意,这同样适用于字符串。

const nums = [3, 5, 6, 82, 1, 4, 3, 5, 82];

const result = nums.reduce((tally, amt) => {
    tally[amt] ? tally[amt]++ : tally[amt] = 1;
    return tally;
}, {});

console.log(result);
//{ "1": 1, "3": 2, "4": 1, "5": 2, "6": 1, "82": 2 }

最初,我们有一个数组和将要放入其中的对象。在 reducer 中,我们首先判断这个item是否存在于累加器中,如果是存在,加1。如果不存在,添加这一项并设置为1。最后,请返回每一项出现的次数。然后,我们运行reduce函数,同时传递 reducer 和初始值。

获取一个数组并将其转换为显示某些条件的对象

假设我们有一个数组,我们希望基于一组条件创建一个对象。reduce 在这里非常适用!现在,我们希望从数组中任意一个数字项创建一个对象,并同时显示该数字的奇数和偶数版本。

const nums = [3, 5, 6, 82, 1, 4, 3, 5, 82];

// we"re going to make an object from an even and odd
// version of each instance of a number
const result = nums.reduce((acc, item) => {
  acc[item] = {
    odd: item % 2 ? item : item - 1,
    even: item % 2 ? item + 1 : item
  }
  return acc;
}, {});

console.log(result);

控制台输出结果:

{ "1": { odd: 1, even: 2 },
  "3": { odd: 3, even: 4 },
  "4": { odd: 3, even: 4 },
  "5": { odd: 5, even: 6 },
  "6": { odd: 5, even: 6 },
  "82": { odd: 81, even: 82 } 
}

当我们遍历数组中的每一项时,我们为偶数和奇数创建一个属性,并且基于一个带模数运算符的内联条件,我们要么存储该数字,要么将其递增1。模算符非常适合这样做,因为它可以快速检查偶数或奇数 —— 如果它可以被两个整除,它是偶数,如果不是,它是奇数。

其它资源

在顶部,我提到了其他一些便利的文章,这些文章有助于更熟悉 reducer 的作用。以下是我的最爱:

MDN文档对此非常有用。说真的,这是他们最好的帖子之一,他们也更详细地描述了如果你不提供一个初始值会发生什么,我们在这篇文章中没有提到。

Coding Train

A Drip of JavaScript

谢谢各位小伙伴愿意花费宝贵的时间阅读本文,如果本文给了您一点帮助或者是启发,请不要吝啬你的赞和Star,您的肯定是我前进的最大动力。 https://github.com/YvetteLau/...

关注公众号,加入技术交流群。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/106488.html

相关文章

  • 深入浅出redux学习(-)

    摘要:只要输入的值不变,每次输出都是一样的值。指定位置元素运算操作如可用以下方式代替主要是生成中最核心的对象。描述发生了什么,是响应并对进行更新。生成的对象包含个方法,分别为,和。按照约定,具有字段来表示它的类型。 前言: 一开始接触redux的时候最令我记住的一句话是:You Might Not Need Redux(那我还写这篇文章干嘛?手动滑稽) 回归正题,本文主要是围绕redux...

    xietao3 评论0 收藏0
  • React学习之深入Redux应用框架

    摘要:作为大型应用状态管理最常用的工具。它是一个应用数据流框架,与框架类似。这是触发变化的惟一途径。在这个函数内部,被调用,其作用是监测是的。否则的话,认为只是一个普通的,将通过也就是进一步分发。到此源码的主要部分学习结束。 Redux作为大型React应用状态管理最常用的工具。它是一个应用数据流框架,与Flux框架类似。它是零依赖的,可以配合其他框架或者类库一起使用。虽然在平时的工作中很多...

    张汉庆 评论0 收藏0
  • 纯Redux原理分析

    摘要:调用链中最后一个会接受真实的的方法作为参数,并借此结束调用链。总结我们常用的一般是除了和之外的方法,那个理解明白了,对于以后出现的问题会有很大帮助,本文只是针对最基础的进行解析,之后有机会继续解析对他的封装 前言 虽然一直使用redux+react-redux,但是并没有真正去讲redux最基础的部分理解透彻,我觉得理解明白redux会对react-redux有一个透彻的理解。 其实,...

    sumory 评论0 收藏0
  • 深入redux技术栈

    摘要:另外,内置的函数在经过一系列校验后,触发,之后被更改,之后依次调用监听,完成整个状态树的更新。总而言之,遵守这套规范并不是强制性的,但是项目一旦稍微复杂一些,这样做的好处就可以充分彰显出来。 这一篇是接上一篇react进阶漫谈的第二篇,这一篇主要分析redux的思想和应用,同样参考了网络上的大量资料,但代码同样都是自己尝试实践所得,在这里分享出来,仅供一起学习(上一篇地址:个人博客/s...

    imingyu 评论0 收藏0
  • 深入redux技术栈

    摘要:另外,内置的函数在经过一系列校验后,触发,之后被更改,之后依次调用监听,完成整个状态树的更新。总而言之,遵守这套规范并不是强制性的,但是项目一旦稍微复杂一些,这样做的好处就可以充分彰显出来。 这一篇是接上一篇react进阶漫谈的第二篇,这一篇主要分析redux的思想和应用,同样参考了网络上的大量资料,但代码同样都是自己尝试实践所得,在这里分享出来,仅供一起学习(上一篇地址:个人博客/s...

    VPointer 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<