原文连接:yazeedb.com/posts/learn…
原做者:Yazeed Bzadough前端
根据我学习和教学JavaScript的经验,reduce
是最难掌握的概念之一。在这篇文章里,我将尝试解决一个核心问题...git
reduce
是什么,为何它叫reduce
程序员
根据 Wikipedia, 其中一些包括github
它们都暗示了核心思想。将结构分解为单个值。编程
Reduce
- 一个能够将列表折叠成任意数据类型的函数redux
这就像折叠一个盒子!使用reduce
你能够将数组[1,2,3,4,5]
每一项相加获得数字15
。数组
一般,你须要循环才能将列表”折叠“成数字函数式编程
const add = (x, y) => x + y;
const numbers = [1, 2, 3, 4, 5];
let total = 0;
for(let i = 0; i < numbers.length; i++) {
total = add(total, numbers[i]);
}
console.log(total); // 15
复制代码
可是使用reduce
你能够插入你的add
函数,就能够为你处理循环了函数
const add = (x, y) => x + y;
const numbers = [1, 2, 3, 4, 5];
const total = numbers.reduce(add);
// 15
复制代码
你逐项地折叠1-5便可得到15工具
在深刻探讨以前,我认为先分析reduce
和它著名的同伴-map
和filter
很重要。它们严重掩盖了reduce
的光辉,让它看起来很古怪。
尽管它们各自很受欢迎,但结合使用这三个巨人可让你随意操做列表!
这里咱们作个假设,伪装JavaScript不能使用循环,递归,或者像forEach
,some
,find
等数组方法。只剩下map
,filter
和reduce
三个方法。 可是咱们做为程序员的工做没有改变,咱们的应用程序中仍然须要三种类型的功能。
让咱们看看咱们剩下的工具-map
,filter
,reduce
-如何应对这个挑战。
简而言之,将列表转换成其余列表是前端开发。所以map
涵盖了你的大部分列表工做。
假设咱们的应用程序调用了一个用户列表的API,而且咱们须要在屏幕上显示每一个用户的用户名。只需建立一个返回用户用户名的函数。
const getUserName = (user) => user.name;
复制代码
而后插入map
针对整个列表列表运行。
users.map(getUserName);
// ['Marie', 'Ken', 'Sara', 'Geoff', ...]
复制代码
若是你想删除某些项而生成一个新列表,例如用户搜索他们的联系人列表?只需建立一个基于它的输入返回true
或false
的函数。
const isEven = (x) => x % 2 === 0;
复制代码
而后插入filter
针对整个列表运行。
const numbers = [1, 2, 3, 4, 5];
numbers.filter(isEven);
// [2, 4]
复制代码
当map
和filter
还不够时,你须要你们伙。map
/filter
能作的工做,reduce
都能作,还有其余任何涉及遍历数组的。
例如,你将如何计算用户的总年龄?咱们的用户的年龄是25,22,29和30。
const users = [
{ name: 'Marie', age: 25 },
{ name: 'Ken', age: 22 },
{ name: 'Sara', age: 29 },
{ name: 'Geoff', age: 30 }
];
复制代码
map
和filter
只能返回数组,可是咱们须要的是一个number
!
users.map(?);
users.filter(?);
// Nope! I need a number, not arrays.
复制代码
若是咱们有循环,咱们将遍历用户列表并经过一个计数器统计他们的年龄! 好吧,若是我告诉你使用reduce
会更简单些?
users.reduce((total, currentUser) => total + currentUser.age, 0);
// 106
复制代码
我认为要理解它一个简单的方法就是每一步都用console.log
打印出来
const users = [
{ name: 'Marie', age: 25 },
{ name: 'Ken', age: 22 },
{ name: 'Sara', age: 29 },
{ name: 'Geoff', age: 30 }
];
const reducer = (total, currentUser) => {
console.log('current total:', total);
console.log('currentUser:', currentUser);
// just for spacing
console.log('\n');
return total + currentUser.age;
};
users.reduce(reducer, 0);
复制代码
这里有一张Chrome开发工具的截图。
如你所见,Array.reduce
有两个参数
reducer是完成全部工做的函数。当reduce
循环遍历你的列表时,它提供两个参数给你的reducer
当前值不用说,就像你在常规循环中使用array[i]
同样。不过,累加器是一个听起来很吓人的计算机科学术语,实际上很简单。
当你循环遍历用户列表时,你如何跟踪它们的总年龄?你须要一些计数器变量来保存它。那就是累加器。它将做为最终值在reduce
完成后输出。
在循环的每一步,它提供最后的累加器和当前值给你的reducer。不管reducer返回什么都将成为下一个累加器。当列表遍历完成而且你获得一个reduce后的单一的值, 循环结束。
reduce
的第二个参数初始值是可选的。若是不提供,默认为列表的第一个元素。
若是你要对纯数字求和,那很好。
[1, 2, 3].reduce((total, current) => total + current);
// 6
复制代码
可是若是你使用对象或数组会中断,由于你不该该将这些东西加起来。
[{ age: 1 }, { age: 2 }, { age: 3 }].reduce((total, obj) => total + obj.age, 0);
// 6
// Initial value fixes it.
// 0 + 1 + 2 + 3 = 6
复制代码
我不能理解我没法创造的东西-理查德·费曼(Richard Feynman)
但愿到目前为止,我已经对你有所帮助。如今是时候编写本身的reduce
函数来真正锤炼本身了。
它将是一个有三个参数的函数。
对于这个demo,初始值不是可选的
const reduce = (reducer, initialValue, array) => {
let accumulator = initialValue;
for (let i = 0; i < array.length; i++) {
const currentItem = array[i];
accumulator = reducer(accumulator, currentItem);
}
return accumulator;
};
复制代码
仅需10行代码,6个关键步骤。我将一步一步说明。
reduce
及其三个参数。initialValue
)。该变量每一个循环都将改变。currentItem
)。reducer
并将accumulator
和currentItem
传入,将结果做为新的accumulator
保存。accumulator
更改完成,将其返回。我想谈谈更多关于 reduce
和 reducers
的历史,但不大肯定应该放在哪里。尽管如此,它仍是颇有趣的!
Redux为JavaScript开发人员带来了reducers
的炫酷用法。但它没有发明他们。实际上不清楚是谁创造了该术语,但这里是我参考的一些参考文献。
1952年出版的 这本书 从形而上学的角度讨论了reduce
,将其称为fold。
1960出版的《Lisp程序员手册》中有一章是关于reduce
函数的。
1988年出版的 这本书 谈论如何使用 reduce
将列表转换为其余值。 底线是一个古老的话题。你对计算机科学的研究越多,你就越意识到咱们正在从新包装几十年前发现的概念。
你对计算机科学的研究越多,你就越意识到咱们正在从新包装几十年前发现的概念。 — Yazeed Bzadough (@yazeedBee) October 13, 2019
为了节省时间,咱们就在这里结束。我但愿至少已暗示这reduce
除求和以外的强大功能。
若是您有兴趣,请尝试这些练习(尽请关注后续文章)
reduce
从新实现 Array.map 函数。reduce
从新实现 Array.filter 函数。reduce
从新实现 Array.some 函数。reduce
从新实现 Array.every 函数。reduce
从新实现 Array.find 函数。reduce
从新实现 Array.forEach 函数。reduce
将数组转换为对象。reduce
将二维数组转换为一维数组(flat)。