实现函数柯里化以及函数组合

230 阅读3分钟

前言:让我们通过几个简单的例子,体会函数柯里化以及函函数组合的用处,并且进一步地去实现这两个功能函数吧!

函数柯里化

函数柯里化主要是体现了函数的单一职责原则,也就是说某个函数只做某件事。下面我们例举一个代码例子,来说说函数柯里化的具体提现。

//打印日志的函数
function log(date, type, message) {
  console.log(
    `[${date.getHours()}:${date.getMinutes()}] [${type}]:[${message}]`
  );
}

log(new Date(), "DEBUG", "查找轮播图的bug1"); //[11:37] [DEBUG]:[查找轮播图的bug1]

function log2(date) {
  return function (type) {
    return function (message) {
      console.log(
        `[${date.getHours()}:${date.getMinutes()}] [${type}]:[${message}]`
      );
    };
  };
}
//可以用柯里化复用函数的功能来"定制"函数
var debug = log2(new Date())("DEBUG");
var warn = log2(new Date())("WARN");
debug("这个是debug"); //这样就不需要每次都传入date和type了

warn("这个是warn");

由上图可以看出,使用函数柯里化可以在某个函数需要传入多个参数的时候,实现参数复用的功能。

用bind实现柯里化

bind函数用于绑定函数的this并且可以传入默认参数,如果传入的thisArg是null的话,就使用默认绑定。我们可以利用这个指定默认参数的性质来实现柯里化。

//使用方法1
function sum(num1,num2,num3){
  return num1+num2+num3
}

let curriedSum = sum.bind(null,1,2)
let curriedSum2 = sum.bind(null,1).bind(null,2)
console.log(curriedSum(3),curriedSum2(3))    // 6  6

实现柯里化函数

函数柯里化实现如下(递归实现)。

function jzspcurried(fn) {
  //返回一个可以接收参数的curried函数
  return function curried(...args) {
    //如果这个函数接收的参数个数比fn要求的参数多,就直接执行并且返回结果
    if (fn.length <= args.length) {
      return fn(...args);
    } else {
      //否则就继续返回一个可以接收参数的函数,这个函数的返回结果取决于curried函数的返回结果
      return function (...args2) {
        return curried(...[...args, ...args2]);
      };
    }
  };
}

//使用方法1
function sum(num1,num2,num3){
  return num1+num2+num3
}

let curriedSum = jzspcurried(sum)(1,2)
let curriedSum2 = jzspcurried(sum)(1)(2)
console.log(curriedSum(3),curriedSum2(3))    // 6  6

函数组合

假如我现在有一个需求,先将一个数乘2,再将这个结果平方,最后返回处理结果,我们可能会写出下面的代码。

function dou(count){
	return count*2
}
function sqr(count){
	return count**2
}

let count = 10
console.log(sqr(dou(count)))

但是当获取结果需要经过的流程逐渐变多时,也就是经历的中间逻辑函数的数量变多时,每次都要手动调用就会很繁琐并且可能出错,所以我们可以设计一个函数,传入需要经历的中间函数。这就是组合函数,具体的实现如下。

function compo(...fnc) {
  //传入了几个函数对象
  let length = fnc.length;
  //判断是否全部传入的是function对象
  for (let i = 0; i < length; i++) {
    if (typeof fnc[i] !== "function") {
      throw new TypeError("要求传入的都是函数类型");
    }
  }
  //返回一个接收参数的函数
  return function (...args) {
    let index = 0;
    //如果length不是0的话,就先保存fnc中第一个函数的执行结果res
    //如果length是0的话,就直接返回传入的参数args
    let res = length ? fnc[index].apply(this, args) : args;
    while (++index < length) {
      //在这里将上一个函数的调用结果作为参数进行调用
      res = fnc[index].call(this, res);
    }
    return res;
  };
}