函数柯里化
在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。这个技术由 Christopher Strachey 以逻辑学家 Haskell Curry 命名的,尽管它是 Moses Schnfinkel 和 Gottlob Frege 发明的。
方便的理解
我们将一个接受多个参数的函数fn(a, b, c)转换成可以一个一个接受参数的函数fn(a)(b)(c),如果要执行该函数,所需参数传满即可fn(a)(b)(c)。
举个例子:
let fn1 = function (a, b, c) {
return a + b + c
}
console.log(fn(1, 2, 3)) // 6
let fn2 = currying(fn1);
console.log(fn2(1)) // function(){...}
console.log(fn2(2)) // function(){...}
console.log(fn2(3)) // 6
console.log(fn2(3)(4)(5)) // 12
这里我们将函数fn1带入函数currying中,返回出来的函数fn2就是被柯里化后的函数。
当我们传入前两个参数的时候,函数fn2并不会立即计算,而是返回一个函数。当我们继续向函数内传入参数到达三个时,函数才会返回值。
currying化的好处
1. 参数复用
这个很好理解,在上面的例子中,如果我们将fn2(1)返回的函数复制给一个变量,我们就复用了这里的参数1。
let fn1 = function (a, b, c) {
return a + b + c
}
let fn2 = currying(fn1);
let fn3 = fn2(1)(2);
console.log(fn3(3)) // 6
console.log(fn3(4)) // 7
console.log(fn3(10)) // 13
console.log(fn3(30)) // 33
2. 延迟计算
我们经常使用的Function.prototype.bind的实现机制就是柯里化。
Function.prototype.bind = function (context) {
var _this = this
var args = Array.prototype.slice.call(arguments, 1)
return function() {
return _this.apply(context, args)
}
}
3. 动态生成函数
假设我们有一个函数,需要通过判断来执行不同的代码块。
let on = function(element, event, handler) {
if (document.addEventListener) {
if (element && event && handler) {
element.addEventListener(event, handler, false);
}
} else {
if (element && event && handler) {
element.attachEvent('on' + event, handler);
}
}
}
这样写显然不太优雅,我们换一种写法。
let on = function(isSupport = document.addEventListener, element, event, handler) {
if (isSupport) {
return element.addEventListener(event, handler, false);
} else {
return element.attachEvent('on' + event, handler);
}
}
发现什么了吗?
如果此时我们将on函数柯里化,就可以动态生成针对不同环境的事件监听函数了。
柯里化实现
那么,我们如何来实现一个柯里化的函数呢? 直接上代码吧
function curryIt(fn) {
// 参数fn函数的参数个数
var n = fn.length;
var args = [];
return function(arg) {
args.push(arg);
if (args.length < n) {
return arguments.callee; // 返回这个函数的引用
} else {
return fn.apply(this, args); // 调用函数
}
};
}