javascript的currying函数介绍

  最早期的curry函数有点多态的意味,就是根据函数参数在内部选用分支:

  

复制代码 代码如下:

  //http://www.openlaszlo.org/pipermail/laszlo-user/2005-March/000350.html

  // ★★On 8 Mar 2005, at 00:06, Steve Albin wrote:

  function add(a, b) {

  if (arguments.length < 1) {

  return add;

  } else if (arguments.length < 2) {

  return function(c) { return a + c }

  } else {

  return a + b;

  }

  }

  var myadd = add( 2 );

  var total = myadd(3);

  日本的一个先行者可能在未搞清arguments也能用Array的原生方法转换为数组的时候,用非常复杂的正则与eval搞出一个更接近现代currying意味的函数。

  

复制代码 代码如下:

  function curry(fun) {

  if (typeof fun != 'function') {

  throw new Error("The argument must be a function.");

  }

  if (fun.arity == 0) {

  throw new Error("The function must have more than one argument.");

  }

  var funText = fun.toString();

  var args = /function .*\((.*)\)(.*)/.exec(funText)[1].split(', ');

  var firstArg = args.shift();

  var restArgs = args.join(', ');

  var body = funText.replace(/function .*\(.*\) /, "");

  var curriedText =

  "function (" + firstArg + ") {" +

  "return function (" + restArgs + ")" + body +

  "}";

  eval("var curried =" + curriedText);

  return curried;

  }

  

   [Ctrl+A 全选 注:如需引入外部Js需刷新才能执行]

  接着是闭包的流行,与数组转换arguments的技术的发现,现代currying函数终于粉墨登场,就好像15~17世纪大航海时代的地理大发现,javascript的世界突然间开阔了许多。

  

复制代码 代码如下:

  //一个简单的现代currying函数

  function curry (fn, scope) {

  var scope = scope || window;

  var args = [];

  for (var i=2, len = arguments.length; i < len; ++i) {

  args.push(arguments[i]);

  };

  return function() {

  fn.apply(scope, args);

  };

  }

  一般的currying函数只有两重,执行情况如下,第一次执行参数不足返回内部函数,第二次执行才最终完成。不过针对这参数,我们还是可以做一些文章。看如下函数:

  

复制代码 代码如下:

  function sum(){

  var result=0;

  for(var i=0, n=arguments.length; i<n; i++){

  result += arguments[i];

  }

  return result;

  }

  alert(sum(1,2,3,4,5)); // 15

  这就没有所谓的参数不足问题,传入一个参数,它也计算。但不传入参数呢?无错,区别在于有没有参数。我们可以让它不断执行自身,如果参数存在的情况下。最后在没有参数的情况下,一次过执行。换言之,前面的步骤是用于储存参数。

  

复制代码 代码如下:

  var sum2= curry(sum);

  sum2= sum2(1)(2)(3)(4)(5);

  sum2(); // 15

  比起一般的currying函数,这有点难度。具体看注解:

  

复制代码 代码如下:

  var curry= function(fn){//原函数的参数为函数

  return function(args){//内部函数的参数为数组,由于立即执行,因此直接到第三重去

  //args是相对于第三重内部函数可是全局变量

  var self= arguments.callee;//把自身保存起来(就是那个数组为参数的第二重函数)

  return function(){ //这才是第二次调用的函数

  if(arguments.length){//如果还有要添加的参数

  [].push.apply(args,arguments);//apply把当前传入的所有参数放进args中

  return self(args);

  }else{

  return fn.apply(this,args);//apply的第二参数为数组

  }

  }

  }([]);

  };

  

复制代码 代码如下:

  function sum(){

  var result=0;

  for(var i=0, n=arguments.length; i<n; i++){

  result += arguments[i];

  }

  return result;

  };

  var curry = function(fn){//原函数的参数为函数

  return function(args){//内部函数的参数为数组,由于立即执行,因此直接到第三重去

  var self= arguments.callee;//把自身保存起来

  return function(){ //这才是第二次调用的函数

  if(arguments.length){//如果还有要添加的参数

  [].push.apply(args,arguments);

  return self(args);

  }

  else return fn.apply(this,args);//执行

  }

  }([]);

  };

  var sum2= curry(sum);

  sum2= sum2(1)(2)(3)(4)(5);

  alert(sum2());

  或者每次传入多个参数:

  

复制代码 代码如下:

  function sum(){

  var result=0;

  for(var i=0, n=arguments.length; i<n; i++){

  result += arguments[i];

  }

  return result;

  };

  var curry = function(fn){//原函数的参数为函数

  return function(args){//内部函数的参数为数组,由于立即执行,因此直接到第三重去

  var self= arguments.callee;//把自身保存起来

  return function(){ //这才是第二次调用的函数

  if(arguments.length){//如果还有要添加的参数

  [].push.apply(args,arguments);

  return self(args);

  }

  else return fn.apply(this,args);//执行

  }

  }([]);

  };

  var sum2= curry(sum);

  sum2= sum2(1,2,3);

  sum2= sum2(4,5,6);

  sum2= sum2(7,8,9);

  alert(sum2());

  但上面的函数有不足之处,最后怎么也要放个括号,我们想只要参数足够就返回结果,多出的参数忽略。改进如下:

  

复制代码 代码如下:

  function curry(f) {

  if (f.length == 0) return f;

  function iterate(args) {

  if (args.length <= f.length)

  return f.apply(null, args);

  return function () {

  return iterate(args.concat(Array.prototype.slice.call(arguments)));

  };

  }

  return iterate([]);

  }

  

   [Ctrl+A 全选 注:如需引入外部Js需刷新才能执行]