Array.prototype.reduce 是 JavaScript 中比较实用的一个函数,但是很多人都没有使用过它,因为 reduce 能做的事情其实 forEach 或者 map 函数也能做,而且比 reduce 好理解。但是 reduce 函数还是值得去了解的。

reduce 函数可以对一个数组进行遍历,然后返回一个累计值,它使用起来比较灵活,下面了解一下它的用法。

reduce 接受两个参数,第二个参数可选:

@param {Function} callback 迭代数组时,求累计值的回调函数
@param {Any} initVal 初始值,可选

其中,callback 函数可以接受四个参数:

@param {Any} acc 累计值
@param {Any} val 当前遍历的值
@param {Number} key 当前遍历值的索引
@param {Array} arr 当前遍历的数组

callback 接受这四个参数,经过处理后返回新的累计值,而这个累计值会作为新的 acc 传递给下一个 callback 处理。直到处理完所有的数组项。得到一个最终的累计值。

reduce 接受的第二个参数是一个初始值,它是可选的。如果我们传递了初始值,那么它会作为 acc 传递给第一个 callback,此时 callback 的第二个参数 val 是数组的第一项;如果我们没有传递初始值给 reduce,那么数组的第一项会作为累计值传递给 callback,数组的第二项会作为当前项传递给 callback。

示例:

对数组求和:

let arr = [1, 2, 3];
let res = arr.reduce((acc, v) => acc + v);
console.log(res); // 6

如果我们传递一个初始值:

let arr = [1, 2, 3];
let res = arr.reduce((acc, v) => acc + v, 94);
console.log(res); // 100

利用 reduce 求和比 forEach 更加简单,代码也更加优雅,只需要清楚 callback 接受哪些参数,代表什么含义就可以了。

我们还可以利用 reduce 做一些其他的事情,比如对数组去重:

let arr = [1, 1, 1, 2, 3, 3, 4, 3, 2, 4];
let res = arr.reduce((acc, v) => {if (acc.indexOf(v) < 0) acc.push(v);return acc;
}, []);
console.log(res); // [1, 2, 3, 4]

统计数组中每一项出现的次数:

let arr = ['Jerry', 'Tom', 'Jerry', 'Cat', 'Mouse', 'Mouse'];
let res = arr.reduce((acc, v) => {if (acc[v] === void 0) acc[v] = 1;else acc[v]++;return acc;
}, {});
console.log(res); // {Jerry: 2, Tom: 1, Cat: 1, Mouse: 2}

将二维数组展开成一维数组:

let arr = [[1, 2, 3], 3, 4, [3, 5]];
let res = arr.reduce((acc, v) => {if (v instanceof Array) {return [...acc, ...v];} else {return [...acc, v];}
});
console.log(res); // [1, 2, 3, 3, 4, 3, 5]

由此可以看出,reduce 函数还是很实用的,但是 reduce 函数兼容性不是特别好,只支持到 IE 9,如果要在 IE 8 及以下使用的话就不行了,所以我们可以自己实现一下,还可以对其做一下扩展,使其能够遍历对象。

首先可以实现一个最基础的 each 函数,作为我们 reduce 的基础:

/*** 遍历对象或数组,对操作对象的属性或元素做处理* @param {Object|Array} param 要遍历的对象或数组* @param {Function} callback 回调函数*/
function each(param, callback) {// ...省略参数校验if (param instanceof Array) {for (var i = 0; i < param.length; i++) {callback(param[i], i, param);}} else if (Object.prototype.toString.call(param) === '[object Object]') {for (var val in param) {callback(param[val], val, param);}} else {throw new TypeError('each 参数错误!');}
}

可以看出 each 可以遍历对象或数组,回调函数接受三个参数:

@param {Any} v 当前遍历项
@param {String|Number} k 当前遍历的索引或键
@param {Object|Array} o 当前遍历的对象或者数组

有了这个基础函数,我们可以开始实现我们的 reduce 函数了:

/*** 迭代数组、类数组对象或对象,返回一个累计值* @param {Object|Array} param 要迭代的数组、类数组对象或对象* @param {Function} callback 对每一项进行操作的回调函数,接收四个参数:acc 累加值、v 当前项、k 当前索引、o 当前迭代对象* @param {Any} initVal 传入的初始值*/
function reduce(param, callback, initVal) {var hasInitVal = initVal !== void 0;var acc = hasInitVal ? initVal : param[0];each(hasInitVal ? param : Array.prototype.slice.call(param, 1), function(v, k, o) {acc = callback(acc, v, k, o);});return acc;
}

可以看到,我们的 reduce 函数就是在 each 上面封装了一层。根据是否传递了初始值 initVal 来决定遍历的起始项。每次遍历都接受 callback 返回的 acc 值,然后在 reduce 的最后返回 acc 累计值就可以啦!

当然,这部分代码有一个很严重的 bug,导致了我们的 polyfill 毫无意义,那就是遍历对象时的 for...in。这个语法和在 IE <= 9 环境下存在 bug,会无法获得对象的属性值,这就导致我们所实现的 reduce 无法在 IE 9 以下遍历对象,但是遍历数组还是可以的。对于 for...in 的这个 bug,可以参考 underscore 是怎么实现的,这里暂时不研究了~

转载于:https://www.cnblogs.com/DM428/p/10126885.html

Array.prototype.reduce 的理解与实现相关推荐

  1. 理解Array.prototype.reduce()的执行过程

    之前对reduce()一直不理解,今天专门看了一遍MDN文档感觉明白了一些.在此记录一下自己的一些理解. Array.prototype.reduce()方法的作用: 对数组中的每个元素执行一次传入的 ...

  2. Array.prototype.map() 、 Array.prototype.reduce()、Array.prototype.filter()

    文章目录 1. map 2. reduce 3. filter 1. map   map 函数接收一个回调函数作为参数,然后返回一个数组,这个数组中的每个元素都是调用回调函数后返回的结果.如: fun ...

  3. 数组的方法之(Array.prototype.reduce() 方法)

    reduce函数 reduce() 方法对累加器和数组中的每个元素(从左到右)应用一个函数,将其减少为单个值. 对数组中的所有元素调用指定的回调函数.该回调函数的返回值为累积结果,并且此返回值在下一次 ...

  4. Array.prototype.reduce用法

    定义 reduce() 方法对数组种的每个元素执行一个由您提供的 reducer (升序执行),将其接口汇总为单个返回值. 语法 arr.reduce(callback(accumulator, cu ...

  5. 【JavaScript笔记 · 基础篇(五)】Array全家桶(引用数据类型中的数组 / Array对象 / Array.prototype)

    文章目录 一. 引用数据类型中的数组 1.1 概述 1.2 初始化 1.2.1 字面量 1.2.2 构造函数模式 1.3 访问 1.4 length属性 1.5 数组遍历 1.6 类数组对象 1.6. ...

  6. [转] 理解 JavaScript 中的 Array.prototype.slice.apply(arguments)

    假如你是一个 JavaScript 开发者,你可能见到过 Array.prototype.slice.apply(arguments) 这样的用法,然后你会问,这么写是什么意思呢? 这个语法其实不难理 ...

  7. 理解 Array.prototype.slice.apply

    1. 首先,我们都理解在js中改变this引用有三种方法,call(), apply(), bind(): 2. bind方法是改变函数内this引用,简单不再描述; 3. 至于 call() 和 a ...

  8. js Array.prototype.slice.call(arguments,0) 理解

    Array.prototype.slice.call(arguments,0) 经常会看到这段代码用来处理函数的参数 网上很多复制粘帖说:Array.prototype.slice.call(argu ...

  9. Array.prototype.slice.call(arguments)

    Array.prototype.slice.call(arguments)能将具有length属性的对象转成数组,除了IE下的节点集合(因为ie下的dom对象是以com对象的形式实现的,js对象与co ...

最新文章

  1. JavaScript-语法、关键保留字及变量
  2. Java基础知识强化83:System类之gc()方法(垃圾回收)以及和finalize()区别
  3. 使用事件委托降低重复的事件绑定,从而降低dom操作的对性能的消耗[兼容IE版]
  4. nssl1452-排行榜【数论】
  5. 如何判断是linux/windows库,module或程序debug还是release(转)
  6. asp.net 初步入门使用正则抓取网页信息
  7. ssas表格模型 权限控制_创建第一个SSAS表格模型数据库
  8. Communication System--ZOJ 1409
  9. C# Cookies揭秘 [Asp.Net, Javascript]
  10. python/pytorch中的一些函数介绍
  11. 网站采集工具免费采集发布网站后台
  12. 学生计算机重启删除文件,Delete.On.Reboot(重启时删除无用文件工具)
  13. 静态HTML+CSS 中国高等教育学生信息网(学信网)网站
  14. 如何编写DTD文档类型定义
  15. 职场礼仪之西装十大禁忌
  16. 使用pymysql报错RuntimeError ‘cryptography‘ package is required for sha256_password or caching_sha2_passw
  17. 基于PHP学生成绩查询系统设计与实现 开题报告
  18. 入门神经网络——识别图片是否为猫
  19. android 8(O)预装APP到data/app目录
  20. bilibili爬虫+数据分析

热门文章

  1. 记一次Hbase数据迁移和遇到的问题
  2. c#数据库訪问返回值类型为SqlDataReader时使用using时注意的问题
  3. 2017.4.11 AM
  4. Linux下jetty报java.lang.OutOfMemoryError: PermGen space及Jetty内存配置调优解决方案
  5. QT分页控件,开源,供大家使用
  6. Mysql计算时间差
  7. [待总结]redmine
  8. 详解 Visual C# 数据库编程
  9. LeetCode—220. 存在重复元素 III
  10. linux python3运行,将Python3安装到Linux上并运行