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
}
// case
const 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') return
for (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