JavaScript arrays are an ordered collection of values, and objects are an unordered collection of key, value pairs. The members of both types can be enumerated and a variety of looping mechanisms can be used for doing that, however, these mechanisms are not interchangeable.
When we are dealing with objects, the order of enumeration is not guaranteed. With arrays it is. That’s all there is to know about enumerating collections. Four of the ways to do so are listed below.
- for in
- for each in
- for
- forEach
for in and for each in are perfect for enumerating properties, and values of an object respectively when order does not matter. To give an example, consider this object:
var phone = {
screen: "320x480",
wifi: true,
price: 99
};
for in can be used to enumerate all the properties and once we know the property name, the corresponding value can be accessed as phone[property].
for(var prop in phone) {
console.log(prop); // "screen", "wifi", and "price"
}
The corresponding values can be accessed by using the bracket notation and passing the property name to the object.
for(var prop in phone) {
console.log(phone[prop]); // "320x480", true, 99
}
With the addition of for each in to JavaScript, which is only supported by Firefox at the moment, the above loop can be rewritten as:
for each(var value in phone) {
console.log(value); // "320x480", true, 99
}
The for each in statement works beautifully when used with E4X, but that is for a later post. You wouldn’t use either of the above statements for arrays because firstly order is not guaranteed, and secondly these are used to list all enumerable properties of an object which may not be desirable for Arrays as several frameworks add properties to the Array prototype which will also get enumerated. Consider this example:
var arr = ["a", "b"];
Array.prototype.justHangingOut = true;
for(var prop in arr) {
console.log(prop); // "0", "1", "justHangingOut"
}
Now justHangingOut may just be hanging out with the Array prototype coming from some framework or library, and not necessarily be a part of your code, but it will show up when you try to enumerate an array in the above manner. Just to prove that I am not hallunicating about frameworks extending the Array prototype, here is the list of all methods added to Array.prototype when I run a small piece of code in Chrome’s console for tumblr.com. The following few properties are listed:
0
1
justHangingOut
each
eachSlice
all
every
any
some
collect
map
detect
findAll
select
filter
grep
include
member
inGroupsOf
inject
invoke
max
min
partition
pluck
reject
sortBy
toArray
entries
zip
size
inspect
find
_reverse
_each
clear
first
last
compact
flatten
without
reverse
uniq
intersect
clone
toJSON
Hopefully the above example is sufficient to convince you that it’s a bad idea to try to enumerate an array using a for in or for each in statement. There is a way using the hasOwnProperty method that is available for all objects to find out if a given property belongs to that object or is coming from its prototype chain, but let’s not get into that for now.
So, moving on to enumerating arrays, a native and a somewhat faster way is to use a plain ol’ for loop. Here’s an example:
var foo = [2, 3, 5, 7, 11];
for(var i = 0; i < foo.length; i++) {
console.log(foo[i]); // 2, 3, 5, 7, 11
}
This may be a little more performant than function-based looping, but I really dislike the use of so many indexes. All that is needed is going over each value and logging it, and all those i’s are just noise. So, I prefer the forEach method of an array that’s part of ECMAScript 5th ed. It helps keep the noise levels low.
var foo = [2, 3, 5, 7, 11];
foo.forEach(function(v) {
console.log(v); // 2, 3, 5, 7, 11
});
If your browser does not have native support for forEach, it’s easy to plug it in yourself. The following code from MDC defines the forEach method for enumerating array values if it’s not already present.
if (!Array.prototype.forEach)
{
Array.prototype.forEach = function(fun /*, thisp*/)
{
var len = this.length >>> 0;
if (typeof fun != "function")
throw new TypeError();
var thisp = arguments[1];
for (var i = 0; i < len; i++)
{
if (i in this)
fun.call(thisp, this[i], i, this);
}
};
}
On an unrelated note, although the above example of extending a native object (Array) with user defined functions may be seen as polluting the native object and even outrageous by some, I see nothing wrong with it. In fact, I am all in favor of it when such methods are put in the right place. The forEach method that’s now part of ECMAScript 5 was adapted from the Prototype framework, and it’s very much possible that if Prototype hadn’t “polluted the native Array object”, then the spec would have been a little different today.
I think if we extend the native types judiciously, it makes code a lot more readable and fun. A great example that comes to mind is the extension to Numeric types in Rails with some neat methods that allow code like:
5.days + 2.days
No, I’m not making this up. That is valid code in Rails thanks to ActiveSupport extensions. Of course, we can always write methods that do exactly the same, but then we face the problem of either making too many things global:
days(2) + days(5)
or namespacing them and making code uglier than it should’ve been.
Time.Range.days(2) + Time.Range.days(5)
Note that the first form 3.days uses a loose form of namespacing as it’s only added for Numeric types and not available globally as in the second example days(3). The third example Time.Range.days(3), on the other hand, uses a seemingly well-defined namespace. I would prefer using the first form because it reads like prose, and at the same time doesn’t throw everything into the global namespace.
That said, its largely a matter of developer preference, and factors like type of project, scale of project, number of developers, etc. should also weigh in when deciding whether extension of native-types aka monkey-patching is acceptable for the project. It can be a huge asset of liability in building up your codebase depending on how it’s done.
Think I’ve segway’d enough for one post. Time to sleep.