壹 ❀ 引

稍微有了解JavaScript数组API的同学,对于reduce方法至少有过一面之缘,也许是for与forEach太强大,或者filter,find很实用,在实际开发中我至始至终没使用过reduce方法一次。很巧的是今天再刷面试题的过程中,遇到了一题关于数组操作的的题,相关解析中有人使用到了reduce方法,好吧我承认我看着有点茫然,因为我从未正眼过它,那么今天就给彼此一首歌的时间,让我们好好了解你,关于reduce本文开始。

 贰 ❀ 关于reduce

一个完整的reduce方法应该是这样:

array.reduce(function(accumulator, currentValue, currentIndex, array), initialValue)

它由一个回调函数与一个初始值组成,其中回调函数接受四个参数,我们先解释回调函数形参与initialValue的含义:

1.initialValue [ɪˈnɪʃl]   [ˈvæljuː] 初始值

initialValue 表示reduce方法第一次执行时的初始值,是一个可选值。

2.accumulator [əˈkjuːmjəleɪtər] 累加器;积聚者

accumulator 正如翻译的那样,它是 reduce 方法多次执行的累积结果,accumulator 的初始值分两种情况:

若有提供 initialValue 初始值,第一次循环时 accumulator 的值便为 initialValue,后续循环时 accumulator 为上次循环的返回值。

若未提供initialValue,第一次循环时 accumulator 的值为数组第一项arr[0],后续循环时为上次循环的返回值。

3.currentValue [ˈkɜːrənt] [ˈvæljuː] 当前值

这个应该不难理解,数组循环当前处理的值。currentValue 的初始值也受到initialValue的影响:

若有提供 initialValue 初始值,第一次循环currentValue 的值为数组第一项arr[0],后续变化随索引递增变化。

若未提供initialValue,第一次循环由于arr[0]成了accumulator 的值,所以currentValue 只能从arr[1]开始,后续变化随索引递增。

4.currentIndex

数组循环当前处理值的索引,currentValue 与 currentIndex是同步变化的。

5.array

当前正在被循环的数组。

说的有点糊涂?没关系,我们通过例子循序渐进的来理解,加深这几个参数的印象。先看第一个例子:

let arr = ['e', 'l', 'l', 'o'];
arr.reduce((accumulator, currentValue, currentIndex, array) => {
console.log(accumulator, currentValue, currentIndex, array);
});

我们直接打印出四个参数,咦?为什么 accumulator第一次循环是e,后续循环怎么都是undefined了?

前面说了,由于reduce方法没有提供初始值,所以第一次循环数组的第一项作为了reduce方法的初始值,后续循环中由于没 return操作,导致accumulator拿不到上次返回值,所以就是undefined了。

我们在console后面加上return操作,再看:

return accumulator+currentValue;

这不就有值了吗,所以使用reduce方法得记住,由于reduce是一个对数组累积操作的方法,在使用中一定得记得加return返回你希望累积操作的数据。

那也不对啊,我数组明明有四项,照输出来看reduce方法怎么只执行了三次?

这是因为我们没提供初始值 initialValue ,导致reduce方法将数组的第一项作为了初始值,所以循环第一次是从数组第二项开始的,我们尝试给reduce添加一个默认值:

let arr = ['e', 'l', 'l', 'o'];
arr.reduce((accumulator, currentValue, currentIndex, array) => {
console.log(accumulator, currentValue, currentIndex, array);
return accumulator+currentValue;
},'h');

可以看到有了默认值,数组第一次循环是从索引 0 开始,完完整整的执行了四次。

也不对啊,accumulator不是循环的累加值吗,执行完毕了怎么显示的是 hell,o怎么没加进去?这不是因为我console写在了return前面了么(我故意的),执行完最后一次跳出循环,reduce方法会返回最终的执行结果,我们用一个变量来保存试试,像这样:

let arr = ['e', 'l', 'l', 'o'];
let result = arr.reduce((accumulator, currentValue, currentIndex, array) => {
return accumulator+currentValue;
},'h');
console.log(result);//hello

看,这不就是一个完整的单词 hello了;

其实对于reduce让人疑惑的无非就是initialValue与accumulator,currentValue的关系,这里我们做个小总结:

如果reduce有提供初始值,则循环从索引0开始,此时accumulator就是initialValue,currentValue值就是arr[0];如果reduce未提供初始值,则arr[0]作为初始值赋予给accumulator,循环从索引1开始,currentValue值就是arr[1]了;

 叁 ❀ reduce作用

那么到这里我们详细介绍了reduce方法的参数与执行规则,了解了这些,我们可以用reduce方法做些什么呢?

1.数组求和

reduce方法本意就是用来记录循环的累积结果,用于数组求和是最合适不过了。比如我们要求数组 [1,2,3,4] 的元素之和,用forEach你得这样写:

let total = 0;
[1, 2, 3, 4].forEach(item => total += item);
console.log(total); //

但通过reduce方法就简单的多,我们可以这么写:

let total = [1, 2, 3, 4].reduce((accumulator, current) => accumulator += current); //

假设我们希望求数字90与数组 [ 1,2,3,4] 元素的和呢,那就这么写:

let total = [1, 2, 3, 4].reduce((accumulator, current) => accumulator += current, 90); //

2.数组去重

比如我们要将数组 [1,2,2,4,null,null] 去除掉重复项,用filter可以这样做:

let arr = [1, 2, 2, 4, null, null].filter((item, index, arr) => arr.indexOf(item) === index); // [1,2,4,null]

当然单说实现使用 new Set 更简单:

let arr = [...new Set([1, 2, 2, 4, null, null])]; // [1,2,4,null]

现在我们知道了reduce方法,其实也可以通过reduce去重,像这样:

let arr = [1, 2, 2, 4, null, null].reduce((accumulator, current) => {
return accumulator.includes(current) ? accumulator : accumulator.concat(current);
}, []);

3.数组降维

比如我们要将二维数组 [[1,2],[3,4],[5,6]] 降维成一维数组,最简单的做法是通过flat方法,像这样:

let arr = [[1,2],[3,4],[5,6]].flat();//[1, 2, 3, 4, 5, 6]

通过reduce也挺简单,我们可以结合concat方法,像这样:

let arr = [[1,2],[3,4],[5,6]].reduce((accumulator, current)=>accumulator.concat(current),[]);//[1, 2, 3, 4, 5, 6]

那如果是个多维数组呢,reduce可以这样做:

let arr = [0,[1],[2, 3],[4, [5, 6, 7]]];

let dimensionReduction = function (arr) {
return arr.reduce((accumulator, current) => {
return accumulator.concat(
Array.isArray(current) ?
dimensionReduction(current) :
current
);
}, []);
}
dimensionReduction(arr); //[0, 1, 2, 3, 4, 5, 6, 7]

相对而言,多维数组降维flat会更简单,当然flat存在兼容问题:

let arr = [0,[1],[2, 3],[4, [5, 6, 7]]].flat(Infinity);// [0, 1, 2, 3, 4, 5, 6, 7]

 肆 ❀ 使用注意

在使用reduce方法有一点需要注意,若有初始值但数组为空,或无初始值但数组只有一项时,reduce方法都不会执行。

[].reduce(() => console.log(1), 1); //不会执行
[1].reduce(() => console.log(1)); //不执行

若数组为空且没有初始值,reduce方法报错。

[].reduce(() => console.log(1)); //报错

所以如果没有初始值,你至少得保证数组有2项才能执行;如果给了初始值,你至少得保证数组有一项才能执行。

[1, 2].reduce(() => console.log(1)); //
[1].reduce(() => console.log(1), 1); //

 伍 ❀ 总

通过文本,我们知道了reduce是一个进行累积操作的方法,当我们提供初始值时,循环从0开始,如果不提供,则循环从索引1开始。

我们知道了reduce还有那么点傲娇,如果数组为空且不提供初始值时reduce会报错,如果想reduce执行,你的数组最低标准应该有一项,同时提供默认值(或数组有两项无初始值)。

那么到这里,关于reduce方法参数与基本用法就全部介绍完毕了。

 参考

JS数组reduce()方法详解及高级技巧

MDN

JS reduce()方法详解,使用reduce数组去重的更多相关文章

  1. Js apply 方法 详解

    Js apply方法详解 我在一开始看到JavaScript的函数apply和call时,非常的模糊,看也看不懂,最近在网上看到一些文章对apply方法和call的一些示例,总算是看的有点眉目了,在这 ...

  2. Js apply方法详解,及其apply()方法的妙用

    Js apply方法详解 我在一开始看到javascript的函数apply和call时,非常的模糊,看也看不懂,最近在网上看到一些文章对apply方法和call的一些示例,总算是看的有点眉目了,在这 ...

  3. JS进阶篇--JS数组reduce()方法详解及高级技巧

    基本概念 reduce() 方法接收一个函数作为累加器(accumulator),数组中的每个值(从左到右)开始缩减,最终为一个值. reduce 为数组中的每一个元素依次执行回调函数,不包括数组中被 ...

  4. JS数组reduce()方法详解及高级技巧

    1.语法 arr.reduce(callback,[initialValue]) reduce 为数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素,接受四个参数:初始值(或者上 ...

  5. JavaScript数组的reduce方法详解

    数组经常用到的方法有push.join.indexOf.slice等等,但是有一个经常被我们忽略的方法:reduce,这个方法简直强大的不要不要的. 我们先来看看这个方法的官方概述:reduce()  ...

  6. JavaScript系列--JavaScript数组高阶函数reduce()方法详解及奇淫技巧

    一.前言 reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值. reduce() 可以作为一个高阶函数,用于函数的 compose. reduce()方 ...

  7. ES6中的数组reduce()方法详解

    reduce() 方法对数组中的每个元素执行一个由我们提供的reducer函数(升序执行),将其结果汇总为单个返回值. 1. 语法reduce说明 arr.reduce(callback(accumu ...

  8. [五]java函数式编程归约reduce概念原理 stream reduce方法详解 reduce三个参数的reduce方法如何使用

    reduce-归约 看下词典翻译: 好的命名是自解释的 reduce的方法取得就是其中归纳的含义 java8 流相关的操作中,我们把它理解 "累加器",之所以加引号是因为他并不仅仅 ...

  9. Java8中聚合操作collect、reduce方法详解

    Stream的基本概念 Stream和集合的区别: Stream不会自己存储元素.元素储存在底层集合或者根据需要产生.Stream操作符不会改变源对象.相反,它会返回一个持有结果的新的Stream.3 ...

随机推荐

  1. docker配置mysql主从与django实现读写分离

    一.搭建主从mysql环境 1 下载mysql镜像 docker pull mysql:5.7 2 运行刚下载的mysql镜像文件 # 运行该命令之前可以使用`docker images`是否下载成功 ...

  2. 使用 cAdvisor 主机上的容器

    目录 前言 安装测试 安装 docker 安装docker-ce 启动 cAdvisor 容器 访问测试 prometheus 服务端配置 使用 promtool 检查配置文件 重新加载配置文件 前言 ...

  3. Java标识符(Identifier)(关键字和保留字)

    Java标识符(Identifier) 1. 只能由英文字母(A~Z)或(a~z).下划线(_).美元符号($)和数字(0~9)组成,且开头不能为数字. 2. 区分大小写! 3. 无长度限制! _3_ ...

  4. 小白的springboot之路(五)、集成druid

    0-前言 Druid阿里巴巴开源的一个java数据库连接池,是Java语言中最好的数据库连接池,Druid能够提供强大的监控和扩展功能:集成它能够方便我们对数据库连接进行监控和分析,下面我们来集成它: ...

  5. drf过滤器、分页器、筛选器的应用

    一.drf 提供的过滤器(ordering) views.py from rest_framework.generics import ListAPIView from . import models ...

  6. 华为云ModelArts2.0来袭

    [摘要] modelarts自发布以来,不断地更新增加新的功能来为AI工程师们带来新的服务,在这次的全联接大会上EI服务产品部总经理贾永利宣布--华为云AI重装升级,并重磅发布一站式AI开发管理平台M ...

  7. 消息通知机制(NSNotification和NSNotificationCenter)

    作者:FlyElephant 出处:http://www.cnblogs.com/xiaofeixiang iOS中委托模式和消息机制基本上开发中用到的比较多,一般最开始页面传值通过委托实现的比较多, ...

  8. Mybatis_resultMap的关联方式实现多表查询(一对多)

    a)在 ClazzMapper.xml 中定义多表连接查询 SQL 语句, 一次性查到需要的所有数据, 包括对应学生的信息. b)通过<resultMap>定义映射关系, 并通过<c ...

  9. 记录我的 python 学习历程-Day06 is id == / 代码块 / 集合 / 深浅拷贝

    一.is == id 用法 在Python中,id是内存地址, 你只要创建一个数据(对象)那么就会在内存中开辟一个空间,将这个数据临时加载到内存中,这个空间有一个唯一标识,就好比是身份证号,标识这个空 ...

  10. 【CuteJavaScript】ES2019 新特性汇总

    最近 ECMAScript2019,最新提案完成:tc39 Finished Proposals,我这里也是按照官方介绍的顺序进行整理,如有疑问,可以查看官方介绍啦~ 另外之前也整理了 <ES6 ...