柯里化 Curry
05 May 2016
Reading time ~1 minute
柯里化 Curry
在计算机科学中,柯里化(Curry),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。 简而言之,函数柯里化是指 参数逐渐求值的过程, 更进一步,就是 对高阶函数的降阶处理。
函数柯里化的对偶是Uncurrying
,一种使用匿名单参数函数来实现多参数函数的方法。例如:
var foo = function(a) {
return function(b) {
return a * a + b * b;
}
}
这样调用上述函数:(foo(3))(4)
,或直接foo(3)(4)
。
通用实现
function curry(fn) {
var slice = Array.prototype.slice;
var outerArgs = slice.call(arguments, 1);
return function() {
var innerArgs = slice.call(arguments);
var finalArgs = outerArgs.concat(innerArgs);
return fn.apply(null, finalArgs);
}
}
我们看这个curry函数,显示接受一个参数,那就是需要被柯里化的函数。同时,因为JS神奇的函数传参,我们可以在curry继续放更多的参数,这些参数在函数体内部将以参数列表的形式存在,你可以通过arguments引用这个参数列表。注意,arguments引用的是一个参数列表而不是数组(虽然很像),所以数组的很多方法它都没有。
outerArgs就是获取除了第一个参数之外的参数列表。
而在返回的函数里,innerArgs获取的是调用这个返回函数时传入的所有参数。finalArgs则是拼接这两个数组(注意此时两个参数列表都已经是正宗的数组,所以才可以使用concat方法)。
最后,使用apply,把所有参数组成的列表传入到原来的函数,其实质就是在调用原始的函数,因为此时的参数都已齐全。
实例
var fn = curry(function(a, b, c) { return [a, b, c]; });
// these are all equivalent
fn("a", "b", "c");
fn("a", "b", "c");
fn("a", "b")("c");
fn("a")("b", "c");
fn("a")("b")("c");
//=> ["a", "b", "c"]
性能
柯里化肯定会有一些开销(函数嵌套,比普通函数占更多内存),但性能瓶颈首先来自其它原因(DOM 操作等)。
从另外一个角度分析,不管你用不用柯里化这个思维,你的代码很可能已经步入了更复杂的模式,会有更大的开销。
有关性能的一些事:
- 存取 arguments 对象通常要比存取命名参数要慢一些。
- 一些老版本的浏览器在 arguments.length 的实现上相当慢。
- 使用 fn.apply() 和 fn.call() 要比直接调用 fn() 要慢点。
- 创建大量嵌套作用域和闭包会带来开销,无论是内容还是速度上。
- 大多数瓶颈来自 DOM 操作
总结
函数柯里化给我们带来的是:解决问题的一种逻辑思维方式
。
alcat2008
Dreamer, Practitioner, Incomplete Front-ender