什么是柯里化?

在数学和计算机科学中,柯里化是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。
举个例子:

1
2
3
4
5
6
7
function addFn(a,b){
return a + b
}
add(1,2)//3
//如果现在有一个柯里化函数curriedAdd
let add= curriedAdd(1
add(2)//3

满足以上条件的curriedAdd 的函数可以用以下代码段实现:

1
2
3
4
5
function curriedAdd(x){
return function(y){
return x + y
}
}

柯里化函数实现

函数柯里化的好处:参数复用。本质上是降低通用性,提高适用性。
这个curriedAdd 的实现表明了实现 Currying 的一个基础 —— Currying 延迟求值的特性需要用到 JavaScript 中的作用域——说得更通俗一些,我们需要使用作用域来保存上一次传进来的参数(闭包)。
对curriedAdd抽象化:

1
2
3
4
5
6
7
8
9
//ES5
function curring(fn){
var args = [].slice.call(arguments,1)//类数组对象转数组,截取参数
return function(){
var newArgs = args.concat([].slice.call(arguments)) //合并参数
return fn.apply(this.newArgs) }
}
var addCurry = curry(add, 1);
addCurry(2) // 3
1
2
3
4
5
6
//ES6
function curring(fn,...args1){
return function(...args2){
fn(...args1,...args2)
}
}

以上实现也只是一个简单的curring,而且只适用于两个参数的函数。让我们用递归来实现自动Curring化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//ES5
function trueCurring(fn,args){
length = fn.length
args = args || []
return function(){
var _agrs = args.slice(0),
for(var i = 0;i<arguments.length;i++){
_args.push(arguments[i])
}
if(_args.length < length){
return trueCurring.call(this,fn,_args)
}else{
return fn.apply(this._args)
}
}
}
1
2
3
4
5
6
7
8
9
10
//ES6
function trueCurring(fn,...args){
//...args 剩余全部参数
if(args.length >= fn.length){ //一次全部传完
return fn(...args)
}
return function(...args2){
return trueCurring(fn,...args,...args2)
}
}

实现基本思路:比较多次接受的参数总数与函数定义时的入参数量,当接受参数的数量大于或等于被 Currying 函数的传入参数数量时,就返回计算结果,否则返回一个继续接受参数的函数。

写在最后

工具库Lodsh
函数的bind方法和curring在功能上有很大的相似。