# 柯里化函数

在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。

百度百科的解释还是是比较难懂,直接看例子:

// 普通的add函数
function add(x, y) {
  return x + y
}

// curry后
function curryAdd(x) {
  return function (y) {
    return x + y
  }
}

add(1, 2)           
// 3
curryAdd(1)(2)   
// 3

# 柯里化作用之一:参数复用

// curry参数复用
function curryAdd(x) {
  return function (y) {
    return x + y
  }
}

var curryAdd5 = curryAdd(5)
curryAdd5(1)
//6

上面定义了一个加5的函数,对参数5进行了复用,不需要每次都传。
也可以对正则表达式进行复用,使用时只需要传验证的内容。

// RegExp.test(txt)
// 普通的验证函数
function check(reg, txt) {
  return reg.test(txt)
}
check(/\d+/g, 'test')
//false
check(/[a-z]+/g, 'test')
//true

//curry后的验证函数
function curryCheck(reg){
	return function(txt){
		return reg.test(txt)
	}
}
var isNumber = curryCheck(/\d+/g)
isNumber('211')
//true
var isLetter = curryCheck(/[a-z]+/g)
isLetter('hello')
//true

# 柯里化作用之二:减少重复的if判断

如果有的参数是一个if判断的结果,可以提前执行一次柯里化,确定下来,再次调用的时候,就不需要重复if判断了。

addEvent = (function(){
	if(window.addEventListener){
		return  function(el, type, fn){
			el.addEventListener(type,function(e){
				fn.call(el,e)
			},false)
		}
	}else if(window.attachEvent){
		return function(el, type, fn){
			el.attachEvent("on"+type, function(e){
				fn.call(el,e)
			})
		}
	}
})();
var btn = document.getElementByTagName('button')[0];
addEvent(btn,'click',fn) 
//执行时就不会总是进行if判断了,因为第一次自执行已经判断完了,调用时会从return的函数开始执行。

addEvent自执行后,得出if的结果确定下来,剩余参数通过其返回函数代入。

# 柯里化作用之三:延迟执行

最典型是是bind兼容的实现,执行第一次的时候只是绑定改变上下文环境,等待第二次调用后才执行fun。

var obj = {
  "name": "curry" 
},
fun = function() {
  console.log(this.name);
}.bind(obj);//此时不执行

fun(); // curry

简单实现:执行时不能传参数

Function.prototype.bind = function (context) {
    var that = this
    var args = [].slice.call(arguments, 1)
 
    return function() {
        return that.apply(context, args)
    }
}

//es6
Function.prototype.bind = function (context,...args) {
    var that = this
    return function() {
        return that.apply(context, ...args)
    }
}

思考一下,如果想在执行时另外传参怎么办?

//兼容各个浏览器
if(Function.prototype.bind===undefined){
	Function.prototype.bind = function(context){
		//缓存数组slice方法
		var Slice = [].slice,
			args = Slice.call(arguments, 1);
		//保存当前引用的this
		var that = this;
		//返回新函数
		return function(){
			//将参数数组化
			var addArgs = Slice.call(arguments),
				allArgs = args.concat(addArgs);
			//对当前函数装饰并返回
			return that.apply(context, allArgs)
		}

	}
}

还是上面的例子,我们可以在执行时新增自定义的数据参数了

var obj = {
  "name": "curry" 
},
fun = function() {
  console.log(this.name);
  console.log(arguments)
}.bind(obj);

var detal = {
	text: '一条有价值的信息'
}
fun(detal);
// curry
//[Object]

# 总结

柯里化的使用场景,保存第一次调用,返回定制的函数传入参数再执行。

  • 一部分参数确定,而另一部分参数不确定。比如正则那个示例
  • 传第一次参数执行一个功能,传第二次参数执行另一个功能。比如vue源码里的makeMap

创建对象 map,返回函数,用于后面查找 某字段是否存在 map 中

function makeMap( str,  expectsLowerCase ) {    
    var map = Object.create(null);   
    var list = str.split(',');    
    for (var i = 0; i < list.length; i++) {
        map[list[i]] = true;
    }    
    return expectsLowerCase ?        
        function(val) { return map[val.toLowerCase()]; } :        
        function(val) { return map[val]; }
}

// 应用
var isUnaryTag = makeMap(   
 'area,base,br,col,embed,frame,hr,img,input,isindex,keygen,' +  
 'link,meta,param,source,track,wbr');

// 查找 area 标签是否存在 上面保存过的 字符串中
isUnaryTag('area')