JS数组Reduce方法功能与用法实例详解


Posted in Javascript onApril 29, 2020

本文实例讲述了JS数组Reduce方法功能与用法。分享给大家供大家参考,具体如下:

概述

一直以来都在函数式编程的大门之外徘徊,要入门的话首先得熟悉各种高阶函数,数组的reduce方法就是其中之一。

reduce方法将会对数组元素从左到右依次执行reducer函数,然后返回一个累计的值。举个形象的例子:你要组装一台电脑,买了主板、CPU、显卡、内存、硬盘、电源...这些零件是组装电脑的必要条件。

装的过程可以简单概括为拆掉每个零件的包装,再装到一起。类比一下reduce函数就可以明白了,那些零件就相当于要执行reduce方法的数组,对每个零件执行拆除包装的加工程序,就是对数组的每个元素执行reducer函数,把这些零件装到一起,就相当于reduce方法的任务,最终组装好的电脑相当于reduce方法的返回值。

reduce方法接收两个参数,第一个参数是回调函数reducer,第二个参数是初始值。reducer函数接收四个参数:

  • Accumulator:MDN上解释为累计器,但我觉得不恰当,按我的理解它应该是截至当前元素,之前所有的数组元素被reducer函数处理累计的结果
  • Current:当前被执行的数组元素
  • CurrentIndex: 当前被执行的数组元素索引
  • SourceArray:原数组,也就是调用reduce方法的数组

如果传入第二个参数,reduce方法会在这个参数的基础上开始累计执行。

概念讲了那么多,那reduce它的执行机制是怎样的呢?别着急,从用法入手一点一点分析。

来个最好理解的例子:数组求和

const arr = [1, 2, 3, 4]
  const accumulator = (total, current, currentIndex, arr) => {
   console.log(total, current, currentIndex, arr);
   return total + current
  }
  console.log(arr.reduce(accumulator))

执行结果如下:

JS数组Reduce方法功能与用法实例详解

很明确,最终的结果就是把所有数组的元素都加起来。值得注意的是,它将数组的第一个元素作为累加的初始值,然后再依次对后边的元素执行reducer函数。

总共执行了三次,得出最终结果。那如果传入初始值,是怎样的执行顺序?

console.log(arr.reduce(accumulator, 3))

结果如下:

JS数组Reduce方法功能与用法实例详解

这次是以传入的初始值作为累加的起点,然后依次对数组的元素执行reducer。

假设对没有初始值的空数组调用reduce方法,则会报错:

Uncaught TypeError: Reduce of empty array with no initial value

一些用法

讲了一些概念,但使用场景更重要,下面来看一下reduce方法都会有哪些用途。

compose函数

compose是函数式编程的一种形式,用于将多个函数合并,上一个函数的返回值作为当前函数的入参,当前函数的返回值再作为下一个函数的入参,这样的效果有点类似于koa中间件的洋葱模型。

[a, b, c, d] => a(b(c(d())))

实际上和累加差不多,只不过把累加操作变成了入参执行,相加的结果变成了执行的返回值。redux的applyMiddleware内就使用了compose,目的是保证最终的dispatch是被所有中间件处理后的结果。

下面来以applyMiddleware中的compose为例,先看用法:

const result = compose(a, b, c)(params)

执行情况是这样:

(params) => a(b(c(params)))

返回的是一个函数,将params作为该函数的入参,被右侧第一个函数执行,执行顺序是从右到左执行,其余的函数的参数都是上一个函数的返回值。

看一下实现:

function compose(...funcs) {
 // funcs是compose要组合的那些函数,arg是componse返回的函数的参数
 if (funcs.length === 0) {
  // 如果没有传入函数,那么直接返回一个函数,函数的返回值就是传进来的参数
  return arg => arg
 }
 if (funcs.length === 1) {
  // 如果只传入了一个函数,直接返回这个函数
  return funcs[0]
 }

 return funcs.reduce((all, current) => {
  return (...args) => {
   return all(current(...args))
  }
 })
}

扁平化数组

const array = [[0, 1], [2, 3], [4, 5]]
const flatten = arr => {
 return arr.reduce((a, b) => {
  return a.concat(b)
 }, [])
}
console.log(flatten(array)); // [0, 1, 2, 3, 4, 5]

来看一下执行过程,

  • 第一次执行,初始值传入[],走到reduce的回调里,参数a就这个[],参数b是数组第一项[0, 1],回调内[].cancat([0, 1])
  • 第二次执行,reduce的回调参数a是上一次回调执行的结果[0, 1],本次继续用它来concat数组的第二项[2, 3],得到结果[0, 1, 2, 3]作为下一次回调执行的参数a继续执行下去
  • ...以此类推

那么假设数组是这样呢?[[0, [111, 222], 1], [2, [333, [444, 555]], 3], [4, 5]],其实加个递归调用就可以

const array = [[0, [111, 222], 1], [2, [333, [444, 555]], 3], [4, 5]]
const flatten = arr => {
 return arr.reduce((a, b) => {
  if (b instanceof Array) {
   return a.concat(flatten(b))
  }
  return a.concat(b)
 }, [])
}
console.log(flatten(array)); // [0, 111, 222, 1, 2, 333, 444, 555, 3, 4, 5]

统计字符串中每个字符出现的次数

每次回调执行的时候,都会往对象中加一个key为字符串,value为出现次数的键值,若已经存储过字符串,那么它的value加1。

const str = 'adefrfdnnfhdueassjfkdiskcddfjds'
const arr = str.split('')
const strObj = arr.reduce((all, current) => {
 if (current in all) {
  all[current]++
 } else {
  all[current] = 1
 }
 return all
}, {})

console.log(strObj)
// {"a":2,"d":7,"e":2,"f":5,"r":1,"n":2,"h":1,"u":1,"s":4,"j":2,"k":2,"i":1,"c":1}

数组去重

const arr = ['1', 'a', 'c', 'd', 'a', 'c', '1']
const afterUnique = arr.reduce((all, current) => {
 if (!all.includes(current)) {
  all.push(current)
 }
 return all
}, [])
console.log(afterUnique); // ["1", "a", "c", "d"]

按照顺序调用promise

这种方式实际上处理的是promise的value,将上一个promise的value作为下一个promise的value进行处理。

const prom1 = a => {
 return new Promise((resolve => {
  resolve(a)
 }))
}
const prom2 = a => {
 return new Promise((resolve => {
  resolve(a * 2)
 }))
}
const prom3 = a => {
 return new Promise((resolve => {
  resolve(a * 3)
 }))
}

const arr = [prom1, prom2, prom3]
const result = arr.reduce((all, current) => {
 return all.then(current)
}, Promise.resolve(10))

result.then(res => {
 console.log(res);
})

实现

通过上面的用法,可以总结出来reduce的特点:

  • 接收两个参数,第一个为函数,函数内会接收四个参数:Accumulator Current CurrentIndex SourceArray,第二个参数为初始值。
  • 返回值为一个所有Accumulator累计执行的结果
Array.prototype.myReduce = function(fn, base) {
  if (this.length === 0 && !base) {
   throw new Error('Reduce of empty array with no initial value')
  }
  for (let i = 0; i < this.length; i++) {
   if (!base) {
    base = fn(this[i], this[i + 1], i, this)
    i++
   } else {
    base = fn(base, this[i], i, this)
   }
  }
  return base
 }

感兴趣的朋友可以使用在线HTML/CSS/JavaScript代码运行工具:http://tools.3water.com/code/HtmlJsRun测试上述代码运行效果。

更多关于JavaScript相关内容可查看本站专题:《JavaScript常用函数技巧汇总》、《javascript面向对象入门教程》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》及《JavaScript数学运算用法总结》

希望本文所述对大家JavaScript程序设计有所帮助。

Javascript 相关文章推荐
jquery乱码与contentType属性设置问题解决方案
Jan 07 Javascript
jQuery fadeTo方法调整图片的透明度使用介绍
May 06 Javascript
js锁屏解屏通过对$.ajax进行封装实现
Jul 31 Javascript
JS中正则表达式只有3种匹配模式(没有单行模式)详解
Jul 28 Javascript
AngularJS教程之MVC体系结构详解
Aug 16 Javascript
KVM虚拟化技术之使用Qemu-kvm创建和管理虚拟机的方法
Oct 05 Javascript
JS键盘版计算器的制作方法
Dec 03 Javascript
AngularJs 禁止模板缓存的方法
Nov 28 Javascript
记React connect的几种写法(小结)
Sep 18 Javascript
微信小程序sessionid不一致问题解决
Aug 30 Javascript
JavaScript实现随机点名器
Mar 25 Javascript
Javascript实现单选框效果
Dec 09 Javascript
JavaScript实现放大镜效果代码示例
Apr 29 #Javascript
React组件设计模式之组合组件应用实例分析
Apr 29 #Javascript
Vue + Node.js + MongoDB图片上传组件实现图片预览和删除功能详解
Apr 29 #Javascript
JS数组push、unshift、pop、shift方法的实现与使用方法示例
Apr 29 #Javascript
JS实现手写 forEach算法示例
Apr 29 #Javascript
Node.js API详解之 querystring用法实例分析
Apr 29 #Javascript
Node.js API详解之 string_decoder用法实例分析
Apr 29 #Javascript
You might like
兼容PHP和Java的des加密解密代码分享
2014/06/26 PHP
基于PHP的简单采集数据入库程序【续篇】
2014/07/30 PHP
php数组随机排序实现方法
2015/06/13 PHP
PHP聊天室简单实现方法详解
2018/12/08 PHP
tp5.1 框架数据库常见操作详解【添加、删除、更新、查询】
2020/05/26 PHP
autoPlay 基于jquery的图片自动播放效果
2011/12/07 Javascript
JavaScript/jQuery 表单美化插件小结
2012/02/14 Javascript
HTML上传控件取消选择
2013/03/06 Javascript
自己封装的javascript事件队列函数版
2014/06/12 Javascript
JavaScript脚本判断蜘蛛来源的方法
2015/09/22 Javascript
JS模拟酷狗音乐播放器收缩折叠关闭效果代码
2015/10/29 Javascript
基于Jquery和html5实现炫酷的3D焦点图动画
2016/03/02 Javascript
jquery插件方式实现table查询功能的简单实例
2016/06/06 Javascript
javascript DOM的详解及实例代码
2017/03/06 Javascript
angularjs实现首页轮播图效果
2017/04/14 Javascript
jQuery实现动画、消失、显现、渐出、渐入效果示例
2018/09/06 jQuery
小程序调用微信支付的方法
2019/09/26 Javascript
深入分析JavaScript 事件循环(Event Loop)
2020/06/19 Javascript
Openlayers测量距离与面积的实现方法
2020/09/25 Javascript
vue3.0中友好使用antdv示例详解
2021/01/05 Vue.js
vue element el-transfer增加拖拽功能
2021/01/15 Vue.js
使用Python的Flask框架构建大型Web应用程序的结构示例
2016/06/04 Python
hmac模块生成加入了密钥的消息摘要详解
2018/01/11 Python
python2.7实现邮件发送功能
2018/12/12 Python
Python数据分析:手把手教你用Pandas生成可视化图表的教程
2018/12/15 Python
Python lambda表达式用法实例分析
2018/12/25 Python
Python学习笔记之图片人脸检测识别实例教程
2019/03/06 Python
django与vue的完美结合_实现前后端的分离开发之后在整合的方法
2019/08/12 Python
wxPython电子表格功能wx.grid实例教程
2019/11/19 Python
用CSS3将你的设计带入下个高度
2009/08/08 HTML / CSS
党员思想汇报范文
2013/12/30 职场文书
阿德的梦教学反思
2014/02/06 职场文书
烹饪大赛策划方案
2014/05/26 职场文书
2014年乡镇工会工作总结
2014/12/02 职场文书
2015新学期开学寄语
2015/02/26 职场文书
实验室安全管理制度
2015/08/05 职场文书