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

 更新时间:2020年04月29日 11:25:07   作者:nero  
这篇文章主要介绍了JS数组Reduce方法功能与用法,结合实例形式详细分析了JS数组Reduce方法操作数组统计、去重等相关操作技巧,需要的朋友可以参考下

本文实例讲述了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))

执行结果如下:

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

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

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

结果如下:

这次是以传入的初始值作为累加的起点,然后依次对数组的元素执行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.jb51.net/code/HtmlJsRun测试上述代码运行效果。

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

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

相关文章

  • 浅析JavaScript中var that=this

    浅析JavaScript中var that=this

    this是Javascript语言的一个关键字。它代表函数运行时,自动生成的一个内部对象,只能在函数内部使用。接下来通过本文给大家分享JavaScript中var that=this所代表的意思,需要的朋友参考下吧
    2017-02-02
  • JS控制图片翻转示例代码(兼容firefox,ie,chrome)

    JS控制图片翻转示例代码(兼容firefox,ie,chrome)

    本篇文章主要介绍了JS控制图片翻转示例代码(兼容firefox,ie,chrome) 需要的朋友可以过来参考下,希望对大家有所帮助
    2013-12-12
  • 一个页面元素appendchild追加到另一个页面元素的问题

    一个页面元素appendchild追加到另一个页面元素的问题

    一般都是自己创建元素然后append到页面的但是如果是页面本身有的元素append到另一个页面元素呢,很多的新手朋友对此问题比较好奇,本人也是如此啊,好了不多说,切入主题,感兴趣的朋友可以了解下哦
    2013-01-01
  • js实现页面图片消除效果

    js实现页面图片消除效果

    这篇文章主要为大家详细介绍了js实现页面图片消除效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-03-03
  • 原生JS实现网络彩票投注效果

    原生JS实现网络彩票投注效果

    分享一个最近模仿市面彩票系统写个小案例,没有使用任何后台,从投注到开奖再到返奖都是用原生JS实现的。
    2016-09-09
  • JavaScript实现函数缓存及应用场景

    JavaScript实现函数缓存及应用场景

    在JavaScript中,可以通过函数缓存来提高函数的执行效率,本文就来介绍一下JavaScript实现函数缓存及应用场景,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01
  • JavaScript 处理树数据结构的方法示例

    JavaScript 处理树数据结构的方法示例

    这篇文章主要介绍了JavaScript 处理树数据结构的方法示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-06-06
  • js定时器的使用(实例讲解)

    js定时器的使用(实例讲解)

    本篇文章主要介绍了js中定时器的使用方法。需要的朋友可以过来参考下,希望对大家有所帮助
    2014-01-01
  • JavaScript实现鼠标悬浮页面切换效果

    JavaScript实现鼠标悬浮页面切换效果

    这篇文章主要为大家详细介绍了JavaScript实现鼠标悬浮页面切换效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • Javascript 同时提交多个Web表单的方法

    Javascript 同时提交多个Web表单的方法

    1 问题来自一位网友的提问: web页面里有多个表单,每个表单对应着某一类数据操作。
    2009-02-02

最新评论