The Iterator Pattern
The key concept of the Iterator Pattern is the use of a function with the single responsibility to traverse a collection and provide access to its items. This function is known as the iterator and provides a way to access the items of the collection, without exposing implementation specifics and the underlying data structure used by the collection object.
Iterators provide a level of encapsulation regarding the way the iteration occurs, decoupling the iteration over the items of a collection from the implementation logic of their consumers.
Note
For more information on the Single Responsibility principle, you can visit http://www.oodesign.com/single-responsibility-principle.html.
How the Iterator Pattern is used by jQuery
As we saw earlier in this chapter, the jQuery core $()
function returns an Array-like object that wraps a collection of page elements and it also provides an iterator function to traverse it and access each element individually. It actually goes one step further and provides a generic helper method jQuery.each()
that can iterate over arrays, Array-like objects, and also object properties.
A more technical description can be found in jQuery API documentation page at http://api.jquery.com/jQuery.each/, where the description of jQuery.each()
reads as follows:
A generic iterator function, which can be used to seamlessly iterate over both objects and arrays. Arrays and Array-like objects with a length property (such as a function's arguments object) are iterated by numeric index, from 0 to length-1. Other objects are iterated via their named properties.
The jQuery.each()
helper function is used internally in several places of the jQuery source code. One of its uses is iterating over the items of a jQuery object and applying manipulations on each of them, as the Composite Pattern suggests. A simple search for the keyword .each(
reveals 56 matches.
Note
As of writing this book, the latest stable version is v2.2.0 and this was used for the above statistics.
We can easily trace its implementation in jQuery's source, either by searching for "each:"
(note that there are two occurrences) or using the jQuery Source Viewer and searching for "jQuery.each()"
(like we did earlier in this chapter):
each: function( obj, callback ) { var length, i = 0; if ( isArrayLike( obj ) ) { length = obj.length; for ( ; i < length; i++ ) { if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { break; } } } else { for ( i in obj ) { if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { break; } } } return obj; }
This helper function is also accessible on any jQuery object by using the same prototypical inheritance that we saw earlier for methods such as .append()
. You can easily find the code that does exactly this, by searching for "jQuery.fn.each()"
in jQuery Source Viewer or directly searching jQuery source code for each:
(note that there are two occurrences):
each: function( callback ) { return jQuery.each( this, callback ); }
Using the method version of ".each()"
enables us to directly iterate over the elements of a jQuery collection object with a more convenient syntax.
The example code that follows showcases how the two flavors of .each()
can be used in our code:
// using the helper function on an array $.each([3, 5, 7], function(index){ console.log(this + 1); }); // using the method on a jQuery object $('.boxContainer .box').each(function(index) { console.log('I\'m box #' + (index + 1)); // index is zero-based });
When executed, the preceding code will log the following on the browser's console:
How it pairs with the Composite Pattern
Since the Composite Pattern encapsulates a collection of items into a single object and the Iterator Pattern can be used to iterate over an abstracted data structure, we can easily characterize these two patterns as complementary.
Where can it be used
The Iterator Pattern can be used in our applications to abstract the way we access items from a data structure. For example, let's suppose we need to retrieve all the items that are greater than 4 from the following tree structure:
var collection = { nodeValue: 7, left: { nodeValue: 4, left: 2, right: { nodeValue: 6, left: 5, right: 9 } }, right: { nodeValue: 9, left: 8 } };
Let's now implement our iterator function. Since tree data structures can have nesting, we end up with the following recursive implementation:
function iterateTreeValues(node, callback) { if (node === null || node === undefined) { return; } if (typeof node === 'object') { if ('left' in node) { iterateTreeValues(node.left, callback); } if ('nodeValue' in node) { callback(node.nodeValue); } if ('right' in node) { iterateTreeValues(node.right, callback); } } else { // its a leaf, so the node is the value callback(node); } }
Finally, we end up with an implementation that looks as follows:
var valuesArray = []; iterateTreeValues(collection, function(value) { if (value > 4) { valuesArray.push(value); } }); console.log(valuesArray);
When executed, the preceding code will log the following on the browser's console:
► Array [ 5, 6, 9, 7, 8, 9 ]
We can clearly see that the iterator simplified our code. We no longer bother with the implementation specifics of the data structure used every time we need to access some items that fulfill certain criteria. Our implementation works on top of the generic API that the iterator exposes, and our implementation logic appears in the callback that we provide to the iterator.
This encapsulation allows us to decouple our implementation from the data structure used, given that an iterator with the same API will be available. For instance, in this example, we can easily change the data structure used to a sorted binary tree or a simple array and preserve our implementation logic the same.