Thoughts from the office by Ed Ball
Thursday, March 31, 2005

When you start passing functions around, you often end up creating anonymous functions that call other functions with specific arguments.

For example, let’s suppose that we’re writing a DHTML calculator, and we have a function WriteDigit that writes the specified digit to the display. To set the onclick handlers of the calculator buttons, we could do something like this:

btnOne.onclick = function () { WriteDigit(1); };
btnTwo.onclick = function () { WriteDigit(2); };

That will work just fine, but there is another way. We can write a function that takes an integer as its argument and returns the anonymous function.

function BindWriteDigit(n)
{
  return function () { WriteDigit(n); };
}
btnOne.onclick = BindWriteDigit(1);
btnTwo.onclick = BindWriteDigit(2);

Why would we do such a thing? Well, one good reason is to avoid unwanted closures. Whenever you create an anonymous function, you’ve got to pay attention to the local variables that will be kept alive, particularly under Internet Explorer, as I alluded to in a previous post. In general, when you are creating anonymous functions that will outlive the function that is creating them, you should minimize the number of local variables in that surrounding function to prevent unwanted closures, accidental modification of local variables used by the anonymous function, and other surprises.

It would be a pain to write a separate binding function for every function that we want to bind; fortunately, we can write a function that takes a function as its first argument and returns an anonymous function that calls that function with its second argument.

function BindArgument(fn, arg)
{
  return function () { return fn(arg); };
}
btnOne.onclick = BindArgument(WriteDigit, 1);
btnTwo.onclick = BindArgument(WriteDigit, 2);

No need to stop with one argument, though; with a little more effort, we can write a function that will bind to any number of arguments by using the apply method.

function BindArguments(fn)
{
  var args = [];
  for (var n = 1; n < arguments.length; n++)
    args.push(arguments[n]);
  return function () { return fn.apply(this, args); };
}
btnOne.onclick = BindArguments(WriteDigit, 1);
btnTwo.onclick = BindArguments(WriteDigit, 2);

This technique comes in very handy, particularly in problems that are less contrived than this one. You can do a lot more than this with a sufficiently complex binding function; more on that in a future post.

Update: Fixed a bug in BindArguments. (Thanks, Bradley!)

3/31/2005 2:50:13 PM (Pacific Daylight Time, UTC-07:00) | Comments [8] | Code | JavaScript#
Search
Archive
Links
Categories
Administration
Blogroll