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

shu.sir.png
reduce接收2个参数,第一个参数是累加处理函数(必传),第二个是初始值(可选),如果第二个参数没有传,就默认把数组的第一项做初始值在累加函数里处理。reduce会用累加函数依次处理数组的每一项,累加函数接收4个参数:始值(上一次回调的返回值),当前元素值,当前索引,原数组。


语法: Array.reduce(callback,[initialValue])
callback函数四个参数:

  • accumulator:上一次调用回调返回的值,或者是提供的初始值(initialValue)
  • currentValue:数组中当前被处理的元素
  • index:当前元素在数组中的索引
  • array:源数组

参数解析:

  1. // 求和&参数打印
  2. var const = [1, 2, 3, 4];
  3. var const = arr.reduce(function(prev, cur, index, arr) {
  4. console.log(prev, cur, index);
  5. return prev + cur;
  6. })
  7. console.log(sum);
  8. // 打印结果
  9. // 1 2 1
  10. // 3 3 2
  11. // 6 4 3
  12. // 10

手写reduce实现

清楚了reduce的行为后,手写一个reduce其实并不难

  1. Array.prototype.myReduce = function(accumulator,initialValue){ // reduce 接收2个参数:累加函数和初始值
  2. if(typeof accumulator !== 'function') return // 累加器不是函数退出
  3. for(let i = 0;i<this.length;i++){ // 通过arr.myReduce来调用的,所以this是当前数组
  4. if(initialValue === undefined) {
  5. initialValue = this[i] // 如果没传initialValue值,就把第一项作为初始值
  6. i++ // 当前的索引要向前移一位,此时被处理的元素是第二个
  7. initialValue = accumulator(initialValue, this[i], i, this) // 执行累加器函数并赋值
  8. } else {
  9. initialValue = accumulator(initialValue, this[i], i, this) // 后续直接执行
  10. }
  11. }
  12. return initialValue
  13. }
  14. // case
  15. const arr = [1, 2, 3, 4];
  16. const sum = arr.myReduce(function(prev, cur, index, arr) {
  17. console.log(prev, cur, index);
  18. return prev + cur;
  19. })
  20. console.log(sum);
  21. // 打印结果:
  22. // 1 2 1
  23. // 3 3 2
  24. // 6 4 3
  25. // 10
  26. // 更简洁的实现
  27. Array.prototype.myReduce = (accumulator, initialValue)=> {
  28. if (typeof accumulator !== 'function') return
  29. for (let i = 0; i < this.length; i++) {
  30. initialValue = initialValue === undefined ? accumulator(this[i], this[++i], i, this) : accumulator(initialValue, this[i], i, this)
  31. }
  32. return initialValue
  33. }

reduce的返回值可以是任意类型的,几乎可以用它代替所有的数组方法,可玩性极强,下面演示几个例子展示一下它的能力。

compose函数

很多开源框架例如express,redux用都用到了compose函数,它用于将多个函数组成一个函数,什么意思呢?举个例子:
case: fn1的返回值是fn2的入参,fn2的返回值是fn3的入参…… 这样就会形成套娃一样的一层套一层,很不美观,可能听着有点懵,代码演示一下。

  1. function sum(a,b) {
  2. return a + b
  3. }
  4. function length(str) {
  5. return str.length
  6. }
  7. function addPrefix(str) {
  8. return '$' + str;
  9. }
  10. let result = addPrefix(len(sum('a','b'))); // 多层嵌套,逻辑不清晰
  11. console.log(result) // $2

compose就是要解决这个问题的,用于返回一个新函数,直接执行这个新函数就可以了。

  1. // 上面的函数执行顺序是有要求的,要用里往外执行,所以用reduceRight会比较方便点,
  2. // reduceRight和reduce一样的逻辑,只是执行方向相反。最后要注意的是,它的返回值是一个函数。
  3. function compose(...fns) {
  4. return function (...args) {
  5. const lastFn = fns.pop() // 取出最后一个函数在下面执行作为值
  6. return fns.reduceRight(function (prev, current) {
  7. return current(prev) // 执行当前函数它的参数是后一个的返回值,如:执行length(str) str是sum的返回值
  8. }, lastFn(...args))
  9. }
  10. }
  11. let finalFn = compose(addPrefix, len, sum)
  12. const result = finalFn('a', 'b');
  13. console.log(result) // $2
  14. // 更简洁的实现
  15. const compose = (...fns) => (...args) => {
  16. let lastFn = fns.pop();
  17. return fns.reduceRight((prev, current) => current(prev), lastFn(...args))
  18. }
  19. // 其实用reduce也可以实现就是可能难理解一点,实际上redux里面的compose就是这样实现的
  20. const compose = (...fns) => fns.reduce((a, b) => (...args) => a(b(...args)))

redux compose https://github.com/reduxjs/redux/blob/master/src/compose.ts#L46