函数合成(compose)与函数柯里化(currying)

735 阅读4分钟

前言

在js中,函数式编程已经成为了未来的趋势。函数式编程通过最小变化使得代码更易理解。 函数式编程倡导利用若干简单的执行单元让计算结果不断渐进,逐层推导复杂的运算。 函数式编程有两个最基本的运算(我认为是最核心的):合成与柯里化。

函数式编程指南

阮一峰函数入门教程

函数合成(compose)

什么是函数合成?

  1. 如果一个值需要经过多个函数操作,才能得到一个最终的值,那么可以将多个函数操作,合并成一个函数,这叫做"函数的合成"
  2. 合成的好处,让代码变得简单而富有可读性,同时通过不同的组合方式,我们可以轻易组合出其他常用函数,让我们的代码更具有表现力。
  // reduce https://www.runoob.com/jsref/jsref-reduce.html
  function compose(...funcs) { // 接收多个函数, 返回一个新函数 接收共同参数

    const res = funcs.reduce((total, currentValue) => { // reduce接收一个函数,并对funcs中的所有值依次调用该函数。该函数前两个参数为,计算之后的值,和当前数组执行的值
      console.log("total", total);
      console.log("currentValue", currentValue);
      return function next(...args) { // 第一次的total 是fn1,下一次是next这个返回函数,值就是调用next后的nextValue,直至funcs循环完,最后再返回next函数给res,给外部调用,其中的值已经存储完成
        console.log("next args", args)
        const nextValue = total(args) + currentValue(args);
        return nextValue;
      }
    })

    console.log("compose reduce", res); // 这里返回的是 next函数,用于接收参数,并且next中是保存了reduce中的执行结果的。
    return res;
  }
  
  function fn1(arg) {
    console.log("fn1", arg);
    return arg; // 
  }

  function fn2(arg) {
    console.log("fn2", arg);
    return arg; // 
  }

  function fn3(arg) {
    console.log("fn3", arg);
    return arg; // 
  }
  
  1. 函数调用
  • 首先调用函数,传入想合成的函数,会返回一个新函数,用于真正执行和接收共同参数。
  • compose中的执行顺序:1. 先执行reduce循环。2. 但是由于里面返回的是一个函数,当前函数并没有调用,所以会等待真正执行的时刻。3. 传入参数并调用时(执行时刻 nextCompose("compose累加"))。
  const nextCompose = compose(fn1, fn2, fn3);
  const nextComposeRes = nextCompose("compose累加"); 
  console.log("nextComposeRes", nextComposeRes); // 最终组合结果
  1. 箭头函数实现compose(简洁明了)
  function composeArrow(...funcs) {
    if (funcs.length === 0) return arg => arg; // 没有函数组合时,直接返回一个新函数,返回值是接收的参数
    if (funcs.length === 1) return funcs[0]; // 当只有一个函数传入时,直接返回该函数,不需要组合。
    return funcs.reduce((t, c) => (...args) => t(args) + c(args)); // 返回新函数接收共同参数,并执行reduce中的调用,再返回最终结果
  }

函数柯里化(currying)

什么是函数柯里化?

柯里化就是将接收多个参数的函数变换为接收一个参数的函数,并且返回一个新函数继续接收新参数,最终返回一个新结果(多参数组成的结果)。

简单的说,将一个多参数函数转换为单参数函数,即可称为柯里化。

柯里化的好处:将函数的颗粒度变得更细,更容易组合,不受限于每次都要传递多个参数,比如多个参数的函数进行合成就非常麻烦,所以这个时候需要进行柯里化转换为单一参数函数,然后就可以合成了。

中间件的核心原理就是使用柯里化,接收单个参数返回新函数(结果)用于下一个中间件继续调用执行。

  // 柯里化之前
  function add(x, y) { // 接收两个参数
    return x + y;
  }

  const add1 = add(1, 2); // 需要传递两个参数,且如果需要更多参数相加需要复杂调用 add(1, 2) + add(5, 8) 十分麻烦。

  console.log("add1", add1);

  // 柯里化之后
  // 接收一个参数 返回一个新函数,通过闭包保存第一次调用时传入的参数,然后与第二次调用时的数据相加,最后返回一个结果。
  // 如需多次调用。composeArrow(addC(1), add(5))(10), composeArrow需要修改第二个共同参数接收形式,...args 是一个数组,相加之后是一个字符串
  function addC(x) {
    return y => x + y;
  }

结语

函数合成与函数柯里化是函数编程的两大利器,要想玩好函数式编程这两个概念必须弄清楚弄透彻。

github文件地址