Thoughts from the office by Ed Ball
Monday, March 28, 2005

Let’s say you want to sort an array of integers.

var array = [ 3, 14, 15, 9, 26 ];

You might think that you could just use the sort method of the Array. You’d be wrong.

array.sort(); // [ 14, 15, 26, 3, 9 ]

The sort method of an Array always sorts strings, which produces the unexpected result shown above. However, you can pass a function to the sort method.

function CompareIntegers(x, y) { return x - y; }
array.sort(CompareIntegers); // [ 3, 9, 14, 15, 26 ]

Now we’re talking. The CompareIntegers function returns a positive integer if x > y, a negative integer if x < y, and zero if x == y, which is what we needed for the sort method.

What if we wanted to sort the numbers in reverse order? Well, we could define a CompareIntegersDescending function, but it’s easier to use an anonymous function.

array.sort(function (x, y) { return y - x; }); // [ 26, 15, 14, 9, 3 ]

Very convenient. Here’s a function that can be used to find an item in an array.

function Find(array, fn)
{
  for (var n = 0; n < array.length; n++)
    if (fn(array[n]))
      return n;
  return -1;
}

The first argument is the array to search; the second argument is a function that returns true when the item has been found. We can use the Find function to find the index of the first item greater than 10:

function FindGreaterThanTen(array)
{
  return Find(array, function (x) { return x > 10; });
}

But what if you want to find the first item greater than an arbitrary number? Remarkably, this works fine:

function FindGreaterThan(array, n)
{
  return Find(array, function (x) { return x > n; });
}

This demonstrates the true power of anonymous functions. The definition of an anonymous function has access to any and every variable that was visible where the function was defined, including local variables. Furthermore, it retains access to those variables as long as the anonymous function lives, even after the calling function has returned! An anonymous function used in this way is called a “closure.”

Whenever you create an anonymous function that is stored in such a way that it will outlive the function call that created it (which is not the case in the example above) you need to keep in mind that every local variable of that function call will live as long as the anonymous function lives. (That’s right – every local variable, not just the variables that the anonymous function happens to use.) If any of the local variables are references to objects that aren’t needed by the anonymous function, this could prevent garbage collection from freeing otherwise unused memory; if the objects reference large amounts of memory, this can quickly become a serious performance problem. If you’re working with Internet Explorer, you should also be aware of a serious memory leak that occurs when reference cycles are created that involve DOM nodes; these cycles are easy to create inadvertently when using anonymous functions as event handlers.

Occasionally, you’ll want an anonymous function to support recursion, in which case you can use arguments.callee to reference the otherwise unnamed function.

var fnClearAll = function (obj)
  {
    for (var key in obj)
      arguments.callee(obj[key]);
    Clear(obj);
  };

Now that I’ve talked about anonymous functions, I can describe my favorite way to enumerate an array – see my next post for a description of the ForEach function.

3/28/2005 1:42:10 PM (Pacific Daylight Time, UTC-07:00) | Comments [0] | Code | JavaScript#
Name
E-mail
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):

Search
Archive
Links
Categories
Administration
Blogroll