# 数组的reduce方法好像也挺有趣

# reduce方法的初步认识

let r = []. reduce(function (previousValue, currentValue, currentIndex, arr) {

    return previousValue + currentValue

}, 1); // reduce的初始值 会把这个值 传递给previousValue 但是如果空数组,则直接将结果返回

# reduce方法的高级应用

# 1、求分数的平均值 (数组中的每个值都是对象)

// 因为最后返回的平均值是 Number类型,所以reduce的第二个参数为 Number
let r = [

    {score:1},
    {score:2},
    {score:3},
    {score:4}

]. reduce((previousValue, currentValue, currentIndex, arr)=>{

    if(arr.length-1 === currentIndex){
        return (previousValue + currentValue.score )/ arr.length
    }
    return previousValue + currentValue.score

}, 0); 

# 2、数组扁平化 [1, [2, [3, [4, [5]]]]]

// 因为最后的返回值是Array类型,所以reduce的第二个参数为 []
function flatten(arr) {
  return arr. reduce((previousValue, currentValue, currentIndex, arr) => {

    if (Array.isArray(currentValue)) {
      return previousValue.concat(flatten(currentValue))
    } else {
      previousValue.push(currentValue)
    }
    return previousValue

  }, [])
}

console. log(flatten([1, [2, [3, [4, [5]]]]]))  // [1, 2, 3, 4, 5]

# 3、把两个数组合并为一个 对象

let keys = ['name', 'age']
let values = ['zf', 10]

let obj = keys. reduce((memo, current, index) => {
  memo[current] = values[index]
  return memo
}, {})

console. log(obj)  // { name: 'zf', age: 10 }

# 4、JS高阶编程技巧(compose函数实现函数调用扁平化) beta 重点

在项目中我们经常遇到类似的情况,比如一个函数执行完的结果,作为下一个函数的实参

例如:

let fn1 = function (x) {
  return x + 10; 
}; 
let fn2 = function (x) {
  return x * 10; 
}; 
let fn3 = function (x) {
  return x / 10; 
}; 

console. log(fn3(fn1(fn2(fn1(5)))))

重要

如果层级结果较多时,需要函数调用扁平化

函数调用扁平化(compose): 如果是多层级嵌套调用的函数,把一个函数调用完,当作另一个函数的实参传递到下一个函数中

compose函数 原理的执行过程

1、例compose()(5),如compose()函数中无函数参数传递进来,则直接返回后面的参数 5

2、例compose(fn1, fn2, fn1, fn3)(5),先执行fn1(5),再将fn1(5)的结果传递到fn2中,如fn2(fn1(5)) => fn2(15),以此类推fn1(fn2(fn1(5))) => fn1(150),后来每一次函数调用的参数都是前一个函数返回的结果,自然我们就想到了Array. prototype. reduce方法

3、我们需要判断每一次的返回值是否为函数,如为函数需要把... args传递进去,否则把前一次的返回结果传递到下一个函数中去。(实际上对于上面这个例子,只有第一次的x为函数)

扁平化函数 从左向右依次执行

/**
 * 
 * @param  {... any} funcs 传递的函数集合
 * @param  {... any} args 第一次调用函数传递的参数集合
 */
function compose(... funcs) {
  
  return function proxy (... args) {

    let len = funcs.length
    if (len === 0) return args // 当没传递函数时,直接返回args
    if (len === 1) return funcs[0](...args) // 当传来一个函数时,只执行它并返回结果

    return funcs.reduce((x, y) => {
      // 只有第一次执行时,x的取值是 function,当取出之后,每次返回的x就有了具体的值
      return typeof x === 'function' ? y(x(...args)) : y(x)       
    })

  }
}

console. log(compose()(5)); //=>5
console. log(compose(fn1)(5)); //=>5+10 = 15
console. log(compose(fn1, fn2)(5)); //=>fn1(5)=15  fn2(15)=150 ... 
console. log(compose(fn1, fn2, fn1, fn3)(5)); //=>16

扁平化函数 从右向左依次执行

function compose(... funcs) {
  
  return function proxy (... args) {

    let len = funcs.length
    if (len === 0) return args // 当没传递函数时,直接返回args
    if (len === 1) return funcs[0](...args) // 当传来一个函数时,只执行它并返回结果

    return funcs.reduceRight((x, y) => {
      // 只有第一次执行时,x的取值是 function,当取出之后,每次返回的x就有了具体的值
      console.log('x',x)
      console.log('y',y)
      // funcs[len-1](...args) 刚开始让funcs数组中的最后一个函数执行,也就是x的值
      return typeof x === 'function' ? y(x(...args)) : y(x)       
    }, funcs[len-1](...args))

  }
}

console. log(compose()(5)); //=>5
console. log(compose(fn1)(5)); //=>5+10 = 15
console. log(compose(fn2, fn1)(5)); //=> x: fn1(5)=15  y:fn1 fn1(15) => 25 ; x: 25 y:fn2 fn2(25) => 250
console. log(compose(fn3, fn1, fn2, fn1)(5)); 

compose(fn3, fn1, fn2, fn1)(5) 是如何执行的

注意: 当reduce函数中的有第二个参数时,x的初始值为reduce函数第二个参数的值

x: 15                       y: fn1 y(x)=25
x: 25                       y: fn2 y(x)=250
x: 250                   y: fn1 y(x)=260
x: 260                   y: fn3 y(x)=26