mixin Enumerable

View source on GitHub →


Enumerable provides a large set of useful methods for enumerations — objects that act as collections of values. It is a cornerstone of Prototype.

Enumerable is a mixin: a set of methods intended not for standaone use, but for incorporation into other objects.

Prototype mixes Enumerable into several classes. The most visible cases are Array and Hash, but you'll find it in less obvious spots as well, such as in ObjectRange and various DOM- or Ajax-related objects.

The context parameter

Every method of Enumerable that takes an iterator also takes the "context object" as the next (optional) parameter. The context object is what the iterator will be bound to — what the keyword this will refer to inside the iterator.

var myObject = {};
 ['foo', 'bar', 'baz'].each(function(name, index) {
  this[name] = index;
}, myObject); // we have specified the context
// -> { foo: 0, bar: 1, baz: 2}

If there is no context argument, the iterator function will execute in the scope from which the Enumerable method itself was called.

Mixing Enumerable into your own objects

So, let's say you've created your very own collection-like object (say, some sort of Set, or perhaps something that dynamically fetches data ranges from the server side, lazy-loading style). You want to be able to mix Enumerable in (and we commend you for it). How do you go about this?

The Enumerable module basically makes only one requirement on your object: it must provide a method named _each (note the leading underscore) that will accept a function as its unique argument, and will contain the actual "raw iteration" algorithm, invoking its argument with each element in turn.

As detailed in the documentation for Enumerable#each, Enumerable provides all the extra layers (handling iteration short-circuits, passing numeric indices, etc.). You just need to implement the actual iteration, as fits your internal structure.

If you're still confused, just have a look at the Prototype source code for Array, Hash, or ObjectRange. They all begin with their own _each method, which should help you grasp the idea.

Once you're done with this, you just need to mix Enumerable in, which you'll usually do before defining your methods, so as to make sure whatever overrides you provide for Enumerable methods will indeed prevail. In short, your code will probably end up looking like this:

 var YourObject = Class.create(Enumerable, {
  initialize: function() { // with whatever constructor arguments you need
    // Your construction code
   _each: function(iterator) {
    // Your iteration code, invoking iterator at every turn
   // Your other methods here, including Enumerable overrides

Then, obviously, your object can be used like this:

var obj = new YourObject();
// Populate the collection somehow
// etc.