Logo Devoh

JavaScript Closures by Example

I've been working with JavaScript for some number of years now, but today was the first day that I truly made use of closures in an intentional way. I'm in the midst of building a dynamic product finder right now, and one of the requirements is to have a table with rows that are sortable. The problem was, once the rows are sorted, their alternating row colors were no longer alternating, but all mixed up according to the sort order.

The table rows needed to be assigned new, alternating class names. Ruby on Rails has a nice little helper built in called cycle that was written just for this very purpose. It takes an array as an argument, and each time it's called it returns the next value from the array. I decided that it would be nice to have that functionality in JavaScript for use on the client side, so here's what I came up with:

var Cycle = Class.create({
  initialize: function (values) {
    this.index = 0;
    this.values = values;
  },

  next: function () {
    if (this.index == this.values.size()) this.index = 0;
    return this.values[this.index++];
  }
});

This ends up looking like this in use:

var rowClasses = new Cycle(['row1', 'row2']);
rowClasses.next(); // 'row1'
rowClasses.next(); // 'row2'
rowClasses.next(); // 'row1'

That works, but it seems like overkill. Closures to the rescue!

var Cycle = function (values) {
  index = 0;

  return function () {
    if (index == values.size()) index = 0;
    return values[index++];
  };
};

Now, this behaves differently than the Rails version, but direct duplication wasn't the intent. Here's how it works:

var nextRowClass = new Cycle(['row1', 'row2']);
nextRowClass(); // 'row1'
nextRowClass(); // 'row2'
nextRowClass(); // 'row1'

As you can see, the function returned from Cycle gets bound to Cycle's scope—and thus the value of index—maintaining state between each execution.

A problem arises, though, when we create more than one Cycle: each Cycle ends up sharing the same index. I've found that the simplest workaround for this is to define the index as another argument to the function.

var Cycle = function (values, index) {
  return function () {
    index = index || 0;
    if (index == values.size()) index = 0;
    return values[index++];
  };
};

Now we can create as many Cycles as we want, and each will have it's own unique index. If you know of a better way to do this, please let me know.