• 首页
  • 关于
    • 前端行知录 photo

      前端行知录

      前端路漫漫,行知方知行

    • Email
    • Github
  • 文章
    • 所有文章
    • 所有标签
  • 作品

柯里化 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 操作

总结

函数柯里化给我们带来的是:解决问题的一种逻辑思维方式。

JavaScript

alcat2008

Dreamer, Practitioner, Incomplete Front-ender

← Mobile APP KPIs React Native 项目打包 →