作者简介:
李中凯老师,8年前端开发,前端负责人,擅长JavaScript/Vue。
公众号:1024译站
掘金文章专栏:https://juejin.im/user/57c7cb8a0a2b58006b1b8666/posts
主要分享:Vue.js, JavaScript,CSS


在所有后 ES6 时代的数组方法中,我觉得最难理解的就是Array.reduce()

从表面上看,它似乎是一个简单无趣的方法,并没有太大作用。 但是在不起眼的外表之下,Array.reduce()实际上是对开发人员工具包的强大而灵活的补充。

今天,我们就来研究一下通过Array.reduce()可以完成的一些有意思的事情。

原理

大部分现代的数组方法都返回一个新的数组,而 Array.reduce() 更加灵活。它可以返回任意值,它的功能就是将一个数组的内容聚合成单个值。

这个值可以是数字、字符串,甚至可以是对象或新数组。这就是一直难住我的部分,我没想到它这么灵活!

用法

Array.reduce()接受两个参数:一个是对数组每个元素执行的回调方法,一个是初始值。

这个回调也接受两个参数:accumulator是当前聚合值,current是数组循环时的当前元素。无论你返回什么值,都将作为累加器提供给循环中的下一个元素。初始值将作为第一次循环的累加器。

var myNewArray = [].reduce(function (accumulator, current) {return accumulator;
}, starting);

让我们来看几个实际例子。

1数组求和

假设你想把一组数字加在一起。使用Array.forEach()大概可以这么做:

var total = 0;[1, 2, 3].forEach(function (num) {total += num;
});

这是Array.reduce()用得最多的例子了。我发现* accumulator *这个单词让人困惑,所以在示例中我改为sum,因为这里就是求和的意思。

var total = [1, 2, 3].reduce(function (sum, current) {return sum + current;
}, 0);

这里传入0作为初始值。在回调中,将当前值加入到 sum,第一轮循环时它的值是初始值0,然后变成1(初始值0加上当前元素值1),然后变成3(累加值 1加上当前元素值 2 ),以此类推。

2组合多个数组方法

假设有一个wizards 数组:

var wizards = [{name: 'Harry Potter',house: 'Gryfindor'},{name: 'Cedric Diggory',house: 'Hufflepuff'},{name: 'Tonks',house: 'Hufflepuff'},{name: 'Ronald Weasley',house: 'Gryfindor'},{name: 'Hermione Granger',house: 'Gryfindor'}
];

你想创建一个仅包含住在 Hufflepuff 的巫师名字的新数组。一个可行的方法是使用Array.filter() 方法获取 house 属性为Hufflepuff的 wizards 。然后用Array.map() 方法创建一个只包含过滤后对象的name 属性的新数组。

var hufflepuff = wizards.filter(function (wizard) {return wizard.house === 'Hufflepuff';
}).map(function (wizard) {return wizard.name;
});

使用Array.reduce() 方法,我们可以用一步得到同样的结果,提高了性能。传递一个空数组[]作为初始值。每次循环时判断wizard.house 是否为Hufflepuff。如果是,就加入到newArr 中(即accumulator),否则啥也不做。

无论判断条件是否成立,最后都返回 newArr 作为下一次循环的accumulator 。

var hufflepuff = wizards.reduce(function (newArr, wizard) {if (wizard.house === 'Hufflepuff') {newArr.push(wizard.name);}return newArr;
}, []);

3从数组生成 HTML 标签

那么,如果想创建一个由住在 Hufflepuff 的巫师组成的无序列表要怎么做呢?这次不是给Array.reduce()传一个空数组作为初始值了,而是一个名为 html的空字符串''

如果wizard.house 等于 Hufflepuff,我们就将wizard.name 用列表项li包裹起来,再拼接到html 字符串里。然后返回html 作为下一次循环的accumulator 。

var hufflepuffList = wizards.reduce(function (html, wizard) {if (wizard.house === 'Hufflepuff') {html += '<li>' + wizard.name + '</li>';}return html;
}, '');

Array.reduce()前后添加无序列表的开始和结束标记,就可以把它插入到 DOM 中了。

var hufflepuffList = '<ul>' + wizards.reduce(function (html, wizard) {if (wizard.house === 'Hufflepuff') {html += '<li>' + wizard.name + '</li>';}return html;
}, '') + '</ul>';

4数组元素分组

lodash 有个 groupBy()方法,可以将数组元素按照某个标准分组。

假设你有一个数字数组。

如果你想把numbers 数组中的元素按照整数部分的值分组,用 lodash 可以这样做:

var numbers = [6.1, 4.2, 6.3];// 返回 {'4': [4.2], '6': [6.1, 6.3]}
_.groupBy(numbers, Math.floor);

如果你有一个单词数组,你想根据 words 中的单词长度分组,你可以这样做:

var words = ['one', 'two', 'three'];// 返回 {'3': ['one', 'two'], '5': ['three']}
_.groupBy(words, 'length');

用 Array.reduce() 实现 groupBy()函数

你可以用Array.reduce() 方法实现同样的功能。

我们来创建一个工具函数groupBy(),接受数组和分组条件作为参数。在groupBy()内部,在数组上执行Array.reduce() ,传一个空对象{}作为初始值,然后返回结果。

var groupBy = function (arr, criteria) {return arr.reduce(function (obj, item) {// 省略代码}, {});
};

在 Array.reduce() 回调函数内部,我们会判断criteria是函数还是 item的属性。然后获取当前item的值。

如果obj 中还不存在这个属性,则创建它,并将一个空数组赋值给它。最后,将item 添加到 key的数组中,再返回该对象作为下一次循环的accumulator 。

var groupBy = function (arr, criteria) {return arr.reduce(function (obj, item) {// 判断criteria是函数还是属性名var key = typeof criteria === 'function' ? criteria(item) : item[criteria];// 如果属性不存在,则创建一个if (!obj.hasOwnProperty(key)) {obj[key] = [];}// 将元素加入数组obj[key].push(item);// 返回这个对象return obj;}, {});
};

5合并数据到单个数组

还记得前面的wizards数组吗?

var wizards = [{name: 'Harry Potter',house: 'Gryfindor'},{name: 'Cedric Diggory',house: 'Hufflepuff'},{name: 'Tonks',house: 'Hufflepuff'},{name: 'Ronald Weasley',house: 'Gryfindor'},{name: 'Hermione Granger',house: 'Gryfindor'}
];

如果还有另一份数据,每个巫师获得的的积分对象:

var points = {HarryPotter: 500,CedricDiggory: 750,RonaldWeasley: 100,HermioneGranger: 1270
};

假设你想把两份数据合并到一个数组,也就是把 points 数值添加到每个巫师对象上。你会怎么做?

Array.reduce() 方法特别适合!

var wizardsWithPoints = wizards.reduce(function (arr, wizard) {// 移除巫师名字中的空格,用来获取对应的 pointsvar key = wizard.name.replace(' ', '');// 如果wizard有points,则加上它,否则设置为0if (points[key]) {wizard.points = points[key];} else {wizard.points = 0;}// 把wizard对象加入到新数组里arr.push(wizard);// 返回这个数组return arr;}, []);

其实这里用Array.map也很方便实现。

6合并数据到单个对象

如果你想合并两个来源的数据到一个对象中,也就是巫师的名字作为属性名,house 和 points 作为属性值,要怎么做呢?同样, Array.reduce() 很合适。

var wizardsAsAnObject = wizards.reduce(function (obj, wizard) {// 移除巫师名字中的空格,用来获取对应的 pointsvar key = wizard.name.replace(' ', '');// 如果wizard有points,则加上它,否则设置为0if (points[key]) {wizard.points = points[key];} else {wizard.points = 0;}// 删除 name 属性delete wizard.name;// 把 wizard 数据添加到新对象中obj[key] = wizard;// 返回该对象return obj;}, {});

总结:Array.reduce() 真香

Array.reduce() 方法从我曾经认为不堪大用的东西,变成我最喜欢的 JavaScript 方法。那么,你应该使用它吗?什么时候可以用?

Array.reduce() 方法有着良好的浏览器支持。所有的现代浏览器都支持,包括 IE9 及以上。移动端浏览器也在很早之前就支持了。如果你还需要支持更老的浏览器,你可以添加一个 polyfill 来支持到 IE6。

Array.reduce()最大的槽点可能就是对于从来没接触过的人来说有点费解。组合使用Array.filter() 和Array.map()执行起来更慢,并且包含多余的步骤,但是更容易阅读,从方法名可以明显看出它要做的事情。

尽管如此,有时候Array.reduce() 也可以让复杂的事情看起来更简单。 groupBy() 工具函数就是个很好的例子。


作者简介:
李中凯老师,8年前端开发,前端负责人,擅长JavaScript/Vue。
公众号:1024译站
掘金文章专栏:https://juejin.im/user/57c7cb8a0a2b58006b1b8666/posts
主要分享:Vue.js, JavaScript,CSS

本文已经获得李中凯老师授权转发,其他人若有兴趣转载,请直接联系作者授权。

自从学会了 Array.reduce() ,再也离不开它相关推荐

  1. JS Array.reduce 对象属性累加

    Array reduce 数组对象使用 无非就是 计算数组元素 相加后的总和 ,看网上给的Demo 全是 [1,2,3,4,6].reduce 这种基本用法, 在实际开发中 数组中一般都是放对象 本次 ...

  2. r720换固态硬盘后如何重装系统_换SSD学会这招后再也不用重装系统了!

    不知道同学们换了固态硬盘后是怎么装系统的,如果重新安装那个时间耗不起吧,估计一天或者半天是必须要的,有没有更容易且不费时间的方法,可以无损的把原来的机械硬盘直接把系统移植到ssd呢?台式机不说,如果是 ...

  3. Array.reduce()的用法与进阶

    Array.reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值 文章目录 1.语法 2.参数解析 3.高阶用法 1.计算数组中没个元素出 ...

  4. 录屏怎么录声音苹果_怎么录屏苹果?学会这招,再也不用担心了

    怎么录屏苹果?目前有很多办公族为了保证办公的安全性和稳定性,都会选择苹果电脑作为办公的设备.但是在使用苹果电脑的过程当中,也会遇到一些比较恼人的问题,那就是怎么录屏苹果?大家都知道,当前很多软件都不兼 ...

  5. reduce使用报错---TypeError: Reduce of empty array with no initial value at Array.reduce (<anonymous>)

    TypeError: Reduce of empty array with no initial value at Array.reduce () 解决:给reduce方法的第二个参数(初始值) 推荐 ...

  6. Array.reduce函数学习(适用大数据)

    文章目录 参考 快速入门 语法 参数说明 例子--数组求和 callback 被调用四次,每次调用的参数和返回值如下表 例子--计算数组中每个元素出现的次数 例子--数组去重 例子--将二维数组转化为 ...

  7. mac mysql打不开闪一下_mac系统上 MySQL Workbench意外退出,再也打不开

    今天遇上怪事,MySQL Workbench在mac 系统上卡死退出后再也打不开, 闹腾半天,重启,重装各种尝试,在网上也找了很多的帖子,最后其实就是当前用户的关于MySQLWorkbench的配置信 ...

  8. 华为手机不小心点了始终_华为手机有一个设置,用过一次就再也离不开了,你打开了吗?...

    最近在各大网络以及朋友圈里,最火的"网红"莫过于任正非先生了,华为受到各路封杀面临着前所未有的巨大挑战,但所表现出的魄力却燃起了大家的激情. 虽然爱国并不是要求大家一定要用华为,不 ...

  9. Firefox火狐浏览器关闭之后再也打不开了 怎么破?

    Firefox火狐浏览器关闭之后再也打不开了 怎么破? 去任务管理器里绞杀FF残余进程! 然后就可以打开了 扩展一下: 在火狐地址栏输入 about:preferences#advanced 回车 网 ...

最新文章

  1. [转][3DSMAX][贴图]如何检查遗失贴图
  2. Keil 5中精简器件支持包,手动安装pack包
  3. LeetCode 97: 交错字符串
  4. 1823政府经济学 (2)
  5. java调用c视频接口_JAVA本地调用(JNI- java调用c)
  6. VB讲课笔记14:二级VB知识点总结
  7. VS Code 翻译插件
  8. 联想高校AI精英挑战赛再下一城,10项目逐鹿华中科大,智能大数据平台夺冠
  9. python实现最大公共子序列
  10. 动态规划:01背包问题、多段图问题
  11. 安装ie9提示未能完成安装_升级Internet Explorer未能完成安装四种解决措施
  12. 凤凰网php,凤凰网房产频道招聘 web 前端工程师、PHP 工程师 15-25k,欢迎简历来砸~...
  13. 浏览器解析jsx_jsx的本质
  14. 如何将PDF英文文档进行翻译?
  15. 机器人开发--二维激光SLAM介绍
  16. 瑞利 随机 matlab,matlab产生瑞利分布随机信号
  17. 安徽师大附中%你赛day3T1 怜香惜玉 解题报告
  18. 前端技术基础--笔记
  19. 房源租赁签约管理系统、租房系统、退租、续租、换租、转租、房源管理、招租系统、租期账单、合同管理、营销推广、租客系统、业主系统、web原型、业务流程 、门禁系统、Axure原型、rp源文件
  20. vue3笔记十(vue3 tsx使用)

热门文章

  1. 【机器学习】KNN算法实现手写板字迹识别
  2. deepin---VMware虚拟机与主机共享文件夹
  3. Java实现评委打分功能
  4. 在CentOS7.6搭建ossec server2.8.3
  5. 第一封情书——祝老婆生日快乐
  6. 如何在运动与休息之间取舍
  7. android wear 支付,中兴智能手表曝光 Android Wear 2.0系统且支持NFC支付
  8. 如何预测药品市场规模
  9. delphi通过odac调用oracle 过程参数为数组,delphi,odac执行存储过程的正确和简单方法...
  10. 招银网科面试题汇总part1