简单易懂的柯里化

320 阅读2分钟

柯里化

概念:将接受多个参数的函数转化为接受部分参数且返回值为处理剩余参数函数的函数。
优势:1. 函数职责单一 2. 逻辑复用
缺点:可能造成内存泄露(因为逻辑复用使用了闭包)

将普通函数柯里化

由概念可知,只需要做两件事即可。

  1. 拆分参数
  2. 返回处理剩余参数的函数
//普通函数
function sum(x, y, z) {
  return x + y + z;
}

//柯里化后的函数
function newSum(x) {
  return function (y) {
    return function (z) {
      return x + y + z;
    };
  };
}

//使用箭头函数简化
const newSum2 = (x) => (y) => (z) => x + y + z;

console.log(sum(1, 2, 3));
console.log(newSum(1)(2)(3));
console.log(newSum2(1)(2)(3));

函数职责单一

函数职责单一的好处:

  1. 扩展性
  2. 可读性
  3. 可维护性
function addMaker(baseNum) {
  //函数职责单一
  //外层函数只负责baseNum的处理
  var newBaseNum = baseNum * 2;
  return function (count) {
    //内层函数只负责相加
    return count + newBaseNum;
  };
}

//逻辑复用
const add10 = addMaker(5);
console.log(add10(10));
console.log(add10(20));
console.log(add10(30));
console.log(add10(40));

分析以上案例。整块代码的逻辑是比较清晰的也就是可读性良好。因此当需要修改代码时,能快速知道该从哪里下手,也即可维护性好。同理,也方便在对应的地方扩展代码,比如对baseNum进行更多的逻辑处理。

逻辑复用

个人认为逻辑复用是柯里化最大的好处。

//普通函数
function log(date, type, message) {
  console.log(
    `[${date.getHours()}:${date.getMinutes()}]:[${type}]:[${message}]`
  );
}
//普通函数调用
log(new Date(), "debug", "A页面有bug");
log(new Date(), "debug", "B页面有bug");
log(new Date(), "debug", "C页面有bug");
//对普通函数进行柯里化(此时已有职责单一优势)
const log2 = (date) => (type) => (message) =>
  console.log(
    `[${date.getHours()}:${date.getMinutes()}]:[${type}]:[${message}]`
  );
//逻辑复用(对date进行复用)
const nowLog = log2(new Date());
nowLog("debug")("A页面有bug");
nowLog("debug")("B页面有bug");
nowLog("debug")("C页面有bug");
//逻辑复用(对date和type进行复用)
const nowLogWithDebug = nowLog("debug");
nowLogWithDebug("A页面有bug");
nowLogWithDebug("B页面有bug");
nowLogWithDebug("C页面有bug");

从以上案例分析可知。

  1. 由于函数的职责单一,当需要对某个函数的逻辑复用时,是相当简单的。
  2. 普通函数调用,每次调用都需要传更多的参数,每次都执行所有逻辑代码。
  3. 逻辑复用只需要调用一次date所在的函数,将结果保存起来。之后直接使用结果函数即可。
  4. 可能该案例不能很好体现逻辑复用的好处。读者可以阅读redux的源码。redux正是用了柯里化,所以使用时,可以对state\dispatch进行逻辑复用,也可以说是定制。

内存泄露

不难发现,柯里化的实现跟闭包紧密相连。可以说没有闭包就没有柯里化。柯里化用到了闭包,自然也有闭包的缺点,那就是内存泄露。