reduce 实现 | compose 运用

796 阅读2分钟

Array.prototype.reduce

数组的实例方法

介绍:

  1. reduce 收敛 把一个数组 转化成 一个值
  2. 调用:array.reduce(callback, init);
  • callback接收四个参数(当前循环下)

    • prev: 初始值 / 数组的前一个元素值
    • next:顾名思义,是数组的下一个元素值
    • index: 当前next对象的下标
    • current: 当前数组
  • init为初始值(optional可选择性传入),callback里的prev会根据是否传入该参数赋值

    • 不传时:prev默认指数组的第一个元素值
    • 传入时:prev指的是init传入的值,next为数组的第一个元素
// 传入时
let arr1 = [ 1, 2, 3, 4, 5 ];
arr1.reduce((prev, next, index) => {
  console.log(prev, next, index);
  return prev + next;
}, 2)
// 2 1 0  下标从0开始
// 3 2 1
// 5 3 2
// 8 4 3
// 12 5 4

// 不传时
let arr1 = [ 1, 2, 3, 4, 5 ];
arr1.reduce((prev, next, index) => {
  console.log(prev, next, index);
  return prev + next;
})
// 1 2 1 下标从1开始
// 3 3 2
// 6 4 3
// 10 5 4

对比上述可见,传入默认值时,数组遍历次数会从第一个元素开始,比不传入时的从第二个元素开始,多遍历一次。

reduce方法实现

Array.prototype.reduce = function(callback, init) {
    // 初始化callback的四个参数
    let prev, index, next, current = this;

    // 判断是否传入callback
    if (!callback) throw new Error("undefined is not a function");

    // 判断是否传入了init 
    // undefined/ null 等同于没有传值
    if (init == null) {
        if (!current.length) throw new Error("Reduce of empty array with no initial value");
        prev = current[0];
        index = 1;
    } else {
        prev = init;
        index = 0;
    }
    next = current[index];

    try {
        while (index < current.length) {
            // 每一轮循环都有返回值
            prev = callback(prev, next, index, current);
            next = current[++index];
        }
    } catch (error) {
        throw error;
    }

    return prev;
};

// [].reduce(); undefined is not a function
// [].reduce(prev=>{}); // Reduce of empty array with no initial value
// console.log([1, 2, 3].reduce((prev, next) => prev + next, 2)); // 8
console.log([1, 2, 3].reduce((prev, next) => prev + next, 2)); // 8

运用

  1. 求和
let arr = [ {count: 3, price: 5}, {count: 3, price: 5}, {count: 3, price: 5} ];
let res = arr.reduce((prev, { count, price }) => prev + count * price , 0)
console.log(res); // 45
  1. compose函数实现 函数返回值作为另一个函数参数的多个函数嵌套调用

实现方法一:reduceRight

function compose(...fns) {
  return function (...args) {
    let lastFn = fns.pop();
    return fns.reduceRight((prev, next) => next(prev), lastFn(...args))
  }
}

// 简写:
const compose = (...fns) => (...args) => {
  let lastFn = fns.pop();
  return fns.reduceRight((prev, next) => next(prev), lastFn(...args))
}

实现方法二:reduce

function compose(...fns) {
  return fns.reduce((prev, next) => {
    return function (...args) {
      return prev((next(...args)))
    }
  })
}

// 简写
const compose = (...fns) => fns.reduce((prev, next) => (...args) => prev((next(...args))));

// 详解
step 1: 传入(getCurreny, len, sum),进入reduce循环体
第一轮:
prev: getCurreny
next: len
返回return: 
function (...args) {
  return prev((next(...args)))
} // 作为下一次的prev

第二轮: 
prev: 
function (...args) {
  return getCurreny((len(...args)))
}
next: sum
返回return: 
function (...args) {
  return function (...args) {
    return getCurreny((len(...args)))
  }((sum(...args))) // ...args = a, b
}

step 2: 传入(a, b), 执行上述返回的代码
function (a, b) {
  return function (...args) {
    return getCurreny((len(...args)))
  }((sum(a, b))) // ...args = a, b
}

step 3: 函数自执行
function ((sum(a, b)) {
    return getCurreny((len((sum(a, b))))
}((sum(a, b))

准备三个函数 相应参数

let sum = (a, b) => a + b;
let len = str => str.length;
let getCurreny = len => `$${len}`;
let a = "hello";
let b = "world";

直接调用

let res = (len(sum(a, b)));
console.log(res); // $10;

compose函数调用

let res = compose(getCurreny, len, sum)(a,b);
console.log(res); // $10;