reduce方法可以搞定的东西,for循环,或者forEach方法有时候也可以搞定,那为啥要用reduce?这个问题,之前我也想过,要说原因还真找不到,唯一能找到的是:通往成功的道路有很多,但是总有一条路是最捷径的,亦或许reduce逼格更高…

reduce接收2个参数,第一个参数是累加处理函数(必传),第二个是初始值(可选),如果第二个参数没有传,就默认把数组的第一项做初始值在累加函数里处理。reduce会用累加函数依次处理数组的每一项,累加函数接收4个参数:始值(上一次回调的返回值),当前元素值,当前索引,原数组。
语法: Array.reduce(callback,[initialValue])
callback函数四个参数:
- accumulator:上一次调用回调返回的值,或者是提供的初始值(initialValue)
- currentValue:数组中当前被处理的元素
- index:当前元素在数组中的索引
- array:源数组
参数解析:
// 求和&参数打印var const = [1, 2, 3, 4];var const = arr.reduce(function(prev, cur, index, arr) {console.log(prev, cur, index);return prev + cur;})console.log(sum);// 打印结果// 1 2 1// 3 3 2// 6 4 3// 10
手写reduce实现
清楚了reduce的行为后,手写一个reduce其实并不难
Array.prototype.myReduce = function(accumulator,initialValue){ // reduce 接收2个参数:累加函数和初始值if(typeof accumulator !== 'function') return // 累加器不是函数退出for(let i = 0;i<this.length;i++){ // 通过arr.myReduce来调用的,所以this是当前数组if(initialValue === undefined) {initialValue = this[i] // 如果没传initialValue值,就把第一项作为初始值i++ // 当前的索引要向前移一位,此时被处理的元素是第二个initialValue = accumulator(initialValue, this[i], i, this) // 执行累加器函数并赋值} else {initialValue = accumulator(initialValue, this[i], i, this) // 后续直接执行}}return initialValue}// caseconst arr = [1, 2, 3, 4];const sum = arr.myReduce(function(prev, cur, index, arr) {console.log(prev, cur, index);return prev + cur;})console.log(sum);// 打印结果:// 1 2 1// 3 3 2// 6 4 3// 10// 更简洁的实现Array.prototype.myReduce = (accumulator, initialValue)=> {if (typeof accumulator !== 'function') returnfor (let i = 0; i < this.length; i++) {initialValue = initialValue === undefined ? accumulator(this[i], this[++i], i, this) : accumulator(initialValue, this[i], i, this)}return initialValue}
reduce的返回值可以是任意类型的,几乎可以用它代替所有的数组方法,可玩性极强,下面演示几个例子展示一下它的能力。
compose函数
很多开源框架例如express,redux用都用到了compose函数,它用于将多个函数组成一个函数,什么意思呢?举个例子:
case: fn1的返回值是fn2的入参,fn2的返回值是fn3的入参…… 这样就会形成套娃一样的一层套一层,很不美观,可能听着有点懵,代码演示一下。
function sum(a,b) {return a + b}function length(str) {return str.length}function addPrefix(str) {return '$' + str;}let result = addPrefix(len(sum('a','b'))); // 多层嵌套,逻辑不清晰console.log(result) // $2
compose就是要解决这个问题的,用于返回一个新函数,直接执行这个新函数就可以了。
// 上面的函数执行顺序是有要求的,要用里往外执行,所以用reduceRight会比较方便点,// reduceRight和reduce一样的逻辑,只是执行方向相反。最后要注意的是,它的返回值是一个函数。function compose(...fns) {return function (...args) {const lastFn = fns.pop() // 取出最后一个函数在下面执行作为值return fns.reduceRight(function (prev, current) {return current(prev) // 执行当前函数它的参数是后一个的返回值,如:执行length(str) str是sum的返回值}, lastFn(...args))}}let finalFn = compose(addPrefix, len, sum)const result = finalFn('a', 'b');console.log(result) // $2// 更简洁的实现const compose = (...fns) => (...args) => {let lastFn = fns.pop();return fns.reduceRight((prev, current) => current(prev), lastFn(...args))}// 其实用reduce也可以实现就是可能难理解一点,实际上redux里面的compose就是这样实现的const compose = (...fns) => fns.reduce((a, b) => (...args) => a(b(...args)))
redux compose https://github.com/reduxjs/redux/blob/master/src/compose.ts#L46
