Contents

柯里化

柯里化(Currying)是一种基于闭包的函数的高阶技术. 而且它不仅被用于 JavaScript, 也在其他编程语言有相同的应用.

柯里化是对函数的转换, 它是指将一个函数从可调用的 f(a, b, c) 转换为可调用的 f(a)(b)(c).

柯里化并不会调用函数, 它只对函数进行转换.

我们将创建一个辅助函数 curry(f), 该函数将对两个参数的函数 f 执行柯里化. 换句话说, 对于两个参数的函数 f(a, b) 执行 curry(f) 会将其转换为以 f(a)(b) 形式运行的函数:

// curry(f) 执行柯里化转换
function curry(f) {
  return function (a) {
    return function (b) {
      // 此函数具有访问 a, b 参数的能力
      return f(a, b);
    };
  };
}

function sum(a, b) {
  return a + b;
}

const curriedSum = curry(sum);
const addOne = curriedSum(1);

addOne(10); // 10 + 1 = 11

实现非常简单: 只有两个包装器(wrapper).

  • curry(func) 的结果就是一个包装器 function(a).
  • 当它被像 curriedSum(1) 这样调用时, 它的参数会被保存在词法环境中, 然后返回一个新的包装器 function(b).
  • 然后这个包装器被以 2 为参数调用, 并且, 它将该调用传递给原始的 sum 函数.

柯里化更高级的实现, 例如 lodash 库的 _.curry, 会返回一个包装器, 该包装器允许函数被正常调用或者以偏函数(partial)的方式调用:

function sum(a, b) {
  return a + b;
}

let curriedSum = _.curry(sum); // 使用来自 lodash 库的 _.curry

curriedSum(1, 2); // 3, 仍可正常调用
curriedSum(1)(2); // 3, 以偏函数的方式调用

柯里化的目的是什么

在函数式编程里, 函数是作为“一等公民”的存在, 可以把它们存在数组里, 当作参数传递, 赋值给变量..

我们举一个实际中的例子来理解:

现在我们要对 blog 进行增、删、改、查进行一次封装, 使其统一在 BlogRequest 命名空间下方便之后进行维护

初始化一个 BlogRequest 的命名空间

export const BlogRequest = {
  add: null,
  del: null,
  update: null,
  query: null,
};

随便封装的一个 request 方法

export function request(url, data) {
  return $.ajax({
    url: '/api/' + url,
    type: 'post',
    data,
    timeout: 30000,
  });
}

我们使用上面的简单的 curry 函数实现 blog request 的封装

const curriedRequest = curry(request);
BlogRequest.add = curriedRequest('blog/add');
BlogRequest.del = curriedRequest('blog/del');
BlogRequest.update = curriedRequest('blog/update');
BlogRequest.query = curriedRequest('blog/query');

非常简单的实现, 现在, 我们就轻松地维护与 blog 相关的请求管理了

参考链接

柯里化(Currying)

函数式编程指北