定义

在计算机科学中,柯里化(英语:Currying),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

就是把 foo(x, y, z) 变成 foo(x)(y)(z)

为什么需要柯里化

柯里化是一种编程的模式,并不是说某些功能只能用柯里化才能实现,而是说在一定的场景下使用柯里化使代码更加简洁、代码复用度更高。

我们以“比较两个数”的例子来说明。

方案一

代码如下:

enter image description here

判断 1 是否大于 10 compare(1, 10, greaterThan) 我们有判断是否大于 10 的需求,并且需要调用 n 次

compare(1, 10, greaterThan) 
compare(2, 10, greaterThan) 
compare(3, 10, greaterThan) 
......

这里的参数 10、greaterThan 是重复代码的,我们优化一下。

方案二

function greaterThan10(value) {
    return compare(value, 10, greaterThan);
}
greaterThan10(1);
greaterThan10(2);
greaterThan10(3);
......

然后我们又有需求,判断是否大于 11 且需要调用 n 次,按这种思路需要不断写 greaterThan* 方法,这是换了一种形式来写重复代码,方案一与方案二没有本质区别。

有没有更简洁的方式?答案是使用柯里化。

方案三

enter image description here

注意:这里用的是 curry2 只支持两个参数。

然后我的代码变为:

const greaterThan10 = greaterThan(10);
greaterThan10(1);
greaterThan10(2);
greaterThan10(3);
......

const greaterThan11 = greaterThan(11);
greaterThan11(1);
greaterThan11(2);
greaterThan11(3);
......

而且我们还能轻松实现判断是否小于某数的功能,代码如下:

const lessThan10 = lessThan(10);
lessThan10(1);
lessThan10(2);
lessThan10(3);
......

lessThan(11)(1);

最后我们来对比下,看看使用柯里化的效果

方案一:

compare(1, 10, greaterThan);
compare(2, 10, greaterThan);             
compare(3, 10, greaterThan); 
......

方案三:

const greaterThan10 = greaterThan(10);
greaterThan10(1);
greaterThan10(2);
greaterThan10(3);
......

不使用柯里化时,三个参数为一个粒度;柯里化可以把三个参数分成三个粒度,在更细的粒度上实现代码的复用,复用度更高。

如何使用柯里化

上文中提到的 curry2 只支持两个参数的情况,如果有更多的参数,不可能一直写 curry3....n,可以使用 Lodash curry,支持任意参数个数。

注意 Lodash curry 方法不仅支持任意参数个数,还支持“部分应用”。

函数式编程也经常看到部分应用,可以把部分应用理解成高级的柯里化,即 foo(x, y, z) 变成 foo(x)(y)(z)foo(x, y)(z)foo(x)(y, z)

Promise 柯里化

对于异步的场景经常会出现如下代码:

enter image description here

成功的回调和 catch 常常会有代码重复,能不能把这部分的逻辑统一处理,并且可以轻易扩展,允许定制化需求。

柯里化主要思想是高阶函数的应用,在提供部分参数时返回一个新的函数,供其他参数继续调用。根据柯里化的主要思想实现 Promise 的柯里化 pcurry 库(传统的柯里化方法并不适用异步的情况)。

代码如下:

enter image description here

即支持统一的错误处理 decorate1,也可以定制化过滤器 decorate2,decorate2 复用了 decorate1 的逻辑。

pcurry 在接收一个函数参数时返回一个函数,并把函数添加到一个队列 flow,在接收一个 promise 对象时按顺序调用函数队列 flow。