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 相关文章推荐
Javascript的&amp;&amp;和||的另类用法
Jul 23 Javascript
JavaScript的instanceof运算符学习教程
Jun 08 Javascript
简单封装js的dom查询实例代码
Jul 08 Javascript
jQuery实现简单的网页换肤效果示例
Sep 18 Javascript
在JS中a标签加入单击事件屏蔽href跳转页面
Dec 16 Javascript
使用jquery判断一个元素是否含有一个指定的类(class)实例
Feb 12 Javascript
SVG描边动画
Feb 23 Javascript
jQuery插件HighCharts实现的2D对数饼图效果示例【附demo源码下载】
Mar 09 Javascript
微信小程序之发送短信倒计时功能
Aug 30 Javascript
利用Node.js批量抓取高清妹子图片实例教程
Aug 02 Javascript
详解vue的双向绑定原理及实现
May 05 Javascript
微信小程序实现日历小功能
Nov 18 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运行SVN命令显示某用户的文件更新记录的代码
2014/01/03 PHP
PHP的foreach中使用引用时需要注意的一个问题和解决方法
2014/05/29 PHP
php实现的Cookies操作类实例
2014/09/24 PHP
php编写简单的文章发布程序
2015/06/18 PHP
PHP开发api接口安全验证操作实例详解
2020/03/26 PHP
一个级联菜单代码学习及removeClass与addClass的应用
2013/01/24 Javascript
js iframe跨域访问(同主域/非同主域)分别深入介绍
2013/01/24 Javascript
JS小功能(列表页面隔行变色)简单实现
2013/11/28 Javascript
JS获取URL中的参数数据
2013/12/05 Javascript
jquery操作HTML5 的data-*的用法实例分享
2014/08/17 Javascript
jQuery div拖拽用法实例
2016/01/14 Javascript
Bootstrap导航条学习使用(二)
2017/02/08 Javascript
JS设置手机验证码60s等待实现代码
2017/06/14 Javascript
基于Cookie常用操作以及属性介绍
2017/09/07 Javascript
three.js 入门案例详解
2018/01/23 Javascript
vue-property-decorator用法详解
2019/12/12 Javascript
node+multer实现图片上传的示例代码
2020/02/18 Javascript
vant 时间选择器--开始时间和结束时间实例
2020/11/04 Javascript
js实现弹窗猜数字游戏
2020/11/26 Javascript
浅谈终端直接执行py文件,不需要python命令
2017/01/23 Python
python 中的list和array的不同之处及转换问题
2018/03/13 Python
python基础知识(一)变量与简单数据类型详解
2019/04/17 Python
python接口自动化(十七)--Json 数据处理---一次爬坑记(详解)
2019/04/18 Python
python redis存入字典序列化存储教程
2020/07/16 Python
python制作微博图片爬取工具
2021/01/16 Python
美国山地自行车、露营、户外装备和服装购物网站:Aventuron
2018/05/05 全球购物
英国伦敦的睡衣品牌:Asceno
2019/10/06 全球购物
娇韵诗俄罗斯官方网站:Clarins俄罗斯
2020/10/03 全球购物
sort命令的作用和用法
2012/11/04 面试题
司法局群众路线教育实践活动开展情况总结
2014/10/25 职场文书
2014年关工委工作总结
2014/11/17 职场文书
优秀班主任材料
2014/12/16 职场文书
大学毕业谢师宴致辞
2015/07/27 职场文书
参加招聘会后的感想
2015/08/10 职场文书
心理健康教育主题班会
2015/08/13 职场文书
python获取带有返回值的多线程
2022/05/02 Python