Javascript函数绑定应用技巧详解
函数绑定要创建一个函数,可以在特定环境中以指定参数调用另一个函数。该技巧常常和回调函数与事件处理程序一起使用,以便在将函数作为变量传递的同时保留代码执行环境。请看以下例子:
var handler = { message: "Event handled", handleClick: function (event) { alert(this.message); } }; var btn = document.getElementById("my-btn"); EventUtil.addHandler(btn, "click", handler.handleClick);
在上面这个例子中,创建了一个叫做handler的对象。handler.handleClick()方法被分配为一个DOM按钮的事件处理程序。当按下该按钮时,就调用该函数,显示一个警告框。虽然貌似警告框应该显示Event handled,然而实际上显示的是undefiend。这个问题在于没有保存handler.handleClick()的环境,所以this对象最后是指向了DOM按钮而非handler。可以如下面的例子所示,使用一个闭包来修正这个问题:
var handler = { message: "Event handled", handleClick: function (event) { alert(this.message); } }; var btn = document.getElementById("my-btn"); EventUtil.addHandler(btn, "click", function (event) { handler.handleClick(event); });
这个解决方案在onclick事件处理程序内使用了一个闭包直接调用handler.handleClick()。当然,这是特定于这段代码的解决方案。创建多个闭包可能会令代码变得难于理解和调试。因此,很多JavaScript库实现了一个可以将函数绑定到指定环境的函数。这个函数一般都叫bind()。
一个简单的bind()函数接受一个函数和一个环境,并返回一个在给定环境中调用给定函数的函数,并且将所有参数原封不动传递过去。语法如下:
function bind(fn, context) { return function () { return fn.apply(context, arguments); }; }
这个函数似乎简单,但其功能非常强大的。在bind()中创建了一个闭包,闭包使用apply()调用传入函数,并给apply()传递context对象和参数。注意这里使用的arguments对象是内部函数的,而非bind()的。当调用返回的函数时,它会在给定环境中执行被传入的函数并给出所有参数。bind()函数按如下方式使用:
var handler = { message: "Event handled", handleClick: function (event) { alert(this.message); } }; var btn = document.getElementById("my-btn"); EventUtil.addHandler(btn, "click", bind(handler.handleClick, handler));
在这个例子中,我们用bind()函数创建了一个保持了执行环境的函数,并将其传给EventUtil.addHandler()。Event对象也被传给了该函数,如下所示:
var handler = { message: "Event handled", handleClick: function (event) { alert(this.message + ":" + event.type); } }; var btn = document.getElementById("my-btn"); EventUtil.addHandler(btn, "click", bind(handler.handleClick, handler));
handler.handleClick()方法和平时一样获得了event对象,因为所有的参数都通过被绑定的函数直接传给了它。
一旦要将某个函数指针以值的形式进行传递,同时该函数必须在特定环境中执行,被绑定函数的效用就凸显出来了。它们主要用于事情处理程序以及setTimeout()和setInterval()。然而,被绑定函数与普通函数相比有更多的开销——它们需要更多的内存,同时也因为多重函数调用稍微慢一点——所以最好只在必要时使用。