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

以上是来自维基百科的名词解释,有点绕。简而言之,柯里化通常也称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果。
因此柯里化的过程是逐步传参,逐步缩小函数的适用范围,逐步求解的过程。

先来看一下柯里化的通用实现:

1
2
3
4
5
6
7
8
function currying(fn) {
var slice = Array.prototype.slice,
__args = slice.call(arguments, 1);
return function () {
var __inargs = slice.call(arguments);
return fn.apply(null, __args.concat(__inargs));
};
}

解读一下这段代码:

  • line 2 & line 3 : Array.prototype.slice.call(arguments)能将具有length属性的对象转成数组,除了IE下的节点集合(因为ie下的dom对象是以com对象的形式实现的,js对象与com对象不能进行转换),所以这里是除了fn之外的参数全部转化为了数组
  • 接下来的 line 4/5/6/7 其实是返回了一个闭包
  • line 5 将闭包函数内的参数转化为数组 __inargs
  • line 6 执行传入的fn函数,其参数为两次传入参数的集合
  • 注:当apply传入null/undefined为第一个参数的时候将执行js全局对象,浏览器中是window,其他环境是global。

柯里化的实用性提现在很多方面:

1 提高适用性

根据以上代码我们来做一个简单的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

function apple(sth){
console.log(`apple ${sth}`);
}

function pineapple(sth){
console.log( `pineapple ${sth}`);
}
function map(handler,list){
return list.map(handler);
}
//数据的每一项进行遍历拼接
map(apple, ['pen','watch','phone']);

map(pineapple, ['pen','watch','phone']);

//加入 柯里化 实现后,可以看到和上面的结果是一致的
var mapApple = currying(map, apple);
mapApple(['pen','watch','phone']);

var mapPine = currying(map, pineapple);
mapPine(['pen','watch','phone']);

由此,可知柯里化不仅仅是提高了代码的合理性,更重的它突出一种思想—降低适用范围,提高适用性。

2 延迟执行

柯里化的另一个应用场景是延迟执行。不断的柯里化,累积传入的参数,最后执行。以下是其通用写法:

1
2
3
4
5
6
7
8
9
10
var curry = function(fn) {
var _args = []
return function cb() {
if (arguments.length == 0) {
return fn.apply(this, _args)
}
Array.prototype.push.apply(_args, arguments);
return cb;
}
}

3 固定易变因素

柯里化特性决定了它这应用场景。提前把易变因素,传参固定下来,生成一个更明确的应用函数。最典型的代表应用,是bind函数用以固定this这个易变对象。

1
2
3
4
5
6
7
8

Function.prototype.bind = function(context) {
var _this = this,
_args = Array.prototype.slice.call(arguments, 1);
return function() {
return _this.apply(context, _args.concat(Array.prototype.slice.call(arguments)))
}
}

 评论