一、函数绑定(bind)
什么是函数绑定?
- 函数绑定需要创建一个函数,可以在特定的this环境中以指定参数调用另一个函数。
- 常与回调函数及事件处理程序一起使用,以便在将函数作为变量传递的同时保留代码执行环境。
var handler = {
message: 'Event handled',
handleClick: function(event) {
console.log(this.message);
}
};
var btn = document.getElementById('my-btn');
EventUtil.addHandler(btn,'click',handler.handleClick);
上述例子中,创建了一个handler的对象。handler.handleClick()方法被分配为一个DOM按钮的事件处理程序。当按下该按钮时,就调用该函数,输出打印数据,按我们看到的讲,会打印输出’Event handled’,然而事实却是输出undefined。这是因为没有保存handler.handleClick()的环境,所以this指向了DOM按钮并非handler。
我们可以使用闭包来修正环境问题。
EventUtil.addHandler(btn,'click',function(event) {
handler.handleClick(event); //恍然大悟,原来平时使用的事件监听就用到了闭包。
});
这个解决方案在onclick事件处理程序内使用了一个闭包直接调用handler.handleClick()。当然,这是鉴于这段代码的解决方案。在其他特定条件下创建多个闭包可能会使代码变得难以理解。因此js库实现了一个可以将函数绑定到指定环境的函数。这个函数就是bind()函数。
bind()
一个简单的bind()函数接受一个函数和一个环境,并返回一个在给定环境中调用给定函数的函数,并且将所有参数原封不动传递过去。
function bind(fn,context) {
return function() {
return fn.apply(context,arguments);
}
}
这个函数看上去简单,但是其功能是非常强大的。在bind()函数中创建了一个闭包,闭包使用apply()调用传入的函数,并给apply()传递context对象和参数。需要注意的是,这里使用的arguments对象是内部函数的,而非bind()的。当调用返回的函数时,它会在给定环境中执行被传入的函数并给出所有参数。
EventUtil.addHandler(btn,'click',bind(handler.handleClick,handler));
二、函数柯里化
与函数绑定紧密相关的是函数柯里化。
- 函数柯里化用于创建已经设置好了一个或者多个参数的函数
- 基本方法和函数绑定一致:使用一个闭包返回一个函数。
- 区别在于,当函数被调用时,返回的函数还需要设置一些需要传入的参数。
function add(num1,num2) {
return nums1 + num2;
}
function curriedAdd(num2) {
return add(5,num2);
}
console.log(add(2,3)) // 5
console.log(curriedAdd(3)) // 8
这面这俩个函数,后者本质上是在任何情况下第一个参数都为5的add()版本。但其实curriedAdd()并非柯里化的函数,但是很好的展示了其概念。
柯里化函数通常由一下步骤动态创建:调用另一个函数并为它传入要柯里化的函数和参数。下面为创建柯里化函数的通用方式。
function curry(fn) {
var args = Array.prototype.slice.call(arguments,1); //获取除第一个参数外的所有参数;第一个参数是要进行柯里化的函数;
return function() {
var innerArgs = Array.prototype.slice.call(arguments); // innerArgs用来存放所有传入的参数(内部函数的参数)
var finalArgs = args.concat(innerArgs); // 有了存放来自外部函数和内部函数的参数数组后,组合为finalArgs
return fn.apply(null,finalArgs); //使用apply()将结果传递给该函数。需要注意的是这个函数没有考虑到执行环境,所以调用apply()时的第一个参数为null。
};
}
function add(num1,num2) {
return nums1 + num2;
}
var curriedAdd = curry(add,5);
console.log(curriedAdd(add,3)) // 8
利用柯里化实现复杂的bind()
function curry(fn,context) {
var args = Array.prototype.slice.call(arguments,2);
return function() {
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.concat(innerArgs);
return fn.apply(context,finalArgs);
};
}
柯里化实现add(1)(2,3)(5)
function add() {
var args = Array.prototype.slice.call(arguments);
return function() {
var innerArgs = Array.prototype.slice.call(arguments);
if(arguments.length == 0) {
var sum = 0;
for(var i in args) {
sum += args[i];
}
return sum;
} else {
var finalArgs = args.concat(innerArgs);
return add.apply(this, finalArgs)
}
}
};
console.log(add(1)(2,3)(5)()); // 11