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 API学Jquery 之三 筛选
Apr 09 Javascript
深入理解JavaScript系列(7) S.O.L.I.D五大原则之开闭原则OCP
Jan 15 Javascript
js/jquery判断浏览器的方法小结
Sep 02 Javascript
解析JavaScript的ES6版本中的解构赋值
Jul 28 Javascript
js实现的动画导航菜单效果代码
Sep 10 Javascript
JavaScript下的时间格式处理函数Date.prototype.format
Jan 27 Javascript
Struts2+jquery.form.js实现图片与文件上传的方法
May 05 Javascript
vue 怎么创建组件及组件使用方法
Jul 27 Javascript
详解如何在nuxt中添加proxyTable代理
Aug 10 Javascript
微信小程序开发注意指南和优化实践(小结)
Jun 21 Javascript
JavaScript定时器设置、使用与倒计时案例详解
Jul 08 Javascript
layui关闭弹窗后刷新主页面和当前更改项的例子
Sep 06 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
ThinkPHP查询语句与关联查询用法实例
2014/11/01 PHP
Laravel 5框架学习之Blade 简介
2015/04/08 PHP
CodeIgniter中使用Smarty3基本配置
2015/06/29 PHP
php curl抓取网页的介绍和推广及使用CURL抓取淘宝页面集成方法
2015/11/30 PHP
在Laravel5.6中使用Swoole的协程数据库查询
2018/06/15 PHP
PHP后期静态绑定实例浅析
2018/12/21 PHP
laravel框架中控制器的创建和使用方法分析
2019/11/23 PHP
实现png图片和png背景透明(支持多浏览器)的方法
2009/09/08 Javascript
javascript void(0)的妙用
2009/10/21 Javascript
js 字符串转化成数字的代码
2011/06/29 Javascript
再谈querySelector和querySelectorAll的区别与联系
2012/04/20 Javascript
基于jquery的多功能软键盘插件
2012/07/25 Javascript
Javascript算符的优先级介绍
2013/03/20 Javascript
js实现连续英文字符自动换行兼容ie6 ie7和firefox
2013/09/06 Javascript
浅谈javascript函数式编程
2015/09/06 Javascript
JavaScript中的定时器之Item23的合理使用
2015/10/30 Javascript
js识别uc浏览器的代码
2015/11/06 Javascript
详解AngularJS中$http缓存以及处理多个$http请求的方法
2016/02/06 Javascript
从0开始学Vue
2016/10/27 Javascript
解决vue热替换失效的根本原因
2018/09/19 Javascript
优雅的使用javascript递归画一棵结构树示例代码
2019/09/22 Javascript
详细介绍解决vue和jsp结合的方法
2020/02/06 Javascript
JavaScript布尔运算符原理使用解析
2020/05/06 Javascript
vue实现页面切换滑动效果
2020/06/29 Javascript
Python  pip安装lxml出错的问题解决办法
2017/02/10 Python
Python实现的简单模板引擎功能示例
2017/09/02 Python
Python爬虫实现全国失信被执行人名单查询功能示例
2018/05/03 Python
python安装virtualenv虚拟环境步骤图文详解
2019/09/18 Python
Python模拟登入的N种方式(建议收藏)
2020/05/31 Python
Django实现内容缓存实例方法
2020/06/30 Python
应届优秀本科大学毕业生自我鉴定
2014/01/21 职场文书
警察先进个人事迹材料
2014/05/16 职场文书
金融系应届毕业生求职信
2014/05/26 职场文书
房地产销售主管岗位职责
2015/02/13 职场文书
golang elasticsearch Client的使用详解
2021/05/05 Golang
Docker部署Mysql8的实现步骤
2022/07/07 Servers