# 柯里化函数
在计算机科学中,柯里化(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')
← node.js批量修改文件名 ES6 →