# 定义

把接受多个参数的函数转换成接受一个单一参数的函数。

举个例子:

var add = function(x, y) {
    return x + y;
}

add(3, 4) // 7

var foo = function(x) {
    return function(y) {
        return x + y
    }
}

foo(3)(4) // 7

原本的add函数是接收两个参数的,可以将它写成函数foo的格式,每次调用只接收一个参数,这就是柯里化达到的目的,随后会讲这么做的好处。如果需要接受3个参数,就可以写成这样:

var foo = function(x) {
    return function(y) {
        return function(z) {
            return x + y + z
        }
    }
}

foo(3)(4)(5) //7

那如果需要传入多个参数,这样的层层嵌套显然是不优雅的,接下来介绍一下一种更为通用的实现。

# 实现

var curry = function(f) {
  var len = f.length
  var args = []
    return function t() {
      // args.push(Array.prototype.slice.call(arguments))
      [].push.apply(args, [].slice.apply(arguments))

      if (args.length >= len) {
      	console.log(args)
         return f.apply(undefined, args)
      } else {
        return t
      }
    }
}

function add (x, y, z) {
	return x + y + z
}

var sum = curry(add)
console.log(sum(1)(2)(3)) // 6

注意内层函数t中的if语句,args是我们在外层函数定义的一个数组变量,用于保存参数,args.length >= len说明接收的参数已经和传入的函数f所需要的参数个数一致了,这个时候就可以执行函数f了。需要注意函数t内被我注释掉的一行,这一行之所以不能写,是因为call和apply的区别,apply的第二个数组就是一个数组,args.push(Array.prototype.slice.call(arguments))实际上是将一个数组对象push进了args中,这样调用add的时候实际输出为“123”。

# 作用

  • 延迟计算(上面例子)
  • 参数复用(例如:正则判断邮箱地址、手机格式)
  • Funtion.prototype.bind也是函数柯里化实现的
  • 动态创建函数
var addEvent = function(el, type, fn, capture) {
     if (window.addEventListener) {
         el.addEventListener(type, function(e) {
             fn.call(el, e);
         }, capture);
     } else if (window.attachEvent) {
         el.attachEvent("on" + type, function(e) {
             fn.call(el, e);
         });
     } 
 };

这个函数意味着,每一次调用addEvent都要执行一次if else,通过函数柯里化可以只需要执行一次:

var addEvent = (function(){
    if (window.addEventListener) {
        return function(el, sType, fn, capture) {
            el.addEventListener(sType, function(e) {
                fn.call(el, e);
            }, (capture));
        };
    } else if (window.attachEvent) {
        return function(el, sType, fn, capture) {
            el.attachEvent("on" + sType, function(e) {
                fn.call(el, e);
            });
        };
    }
})();