spacer
Web Development Tutorials JAVASCRIPT Tutorials
 Developer Newsletter

Tutorials
AJAX
ASP
CGI & Perl
CSS
Flash
HTML
Illustrator
Java
JavaScript
Linux
MySQL
PHP
Photoshop
Python
Wireless
XML
Miscellaneous


Scripts Directory
AJAX Scripts
ASP Scripts
ASP.NET Scripts
CGI & Perl Scripts
Flash Scripts
Java Scripts
JavaScript Scripts
PHP Scripts
Python Scripts
Remotely Hosted Scripts
Tools & Utilities Scripts
XML Scripts

Web Hosting Directory
ASP.NET
Budget
Dedicated Servers
Ecommerce
Linux
Resellers
Shared
Small Business
Windows

Developer Manuals
Learn HTML
Learn PHP
Learn CSS
Learn AJAX
Learn JavaScript
Learn Pear
Free White Papers

Developer Resources
Developer Tools
Developer Content
Survey Software
Dedicated Servers




Enumerating JavaScript Objects

By Dean Edwards
2006-08-22


Enumerating JavaScript Objects

Enumeration lies at the heart of DOM Scripting:

var lists = document.getElementsByTagName("UL");
for (var i = 0; i < lists.length; i++) {
    lists[i].className = "menu";
}


for (var i = 0; i < array.length; i++) {
    print(array[i]);
}


for (var key in object) {
    print(object[key]);
}

Us JavaScripters are forever writing loops like these. To ease the strain on our keyboards Mozilla recently introduced a handy forEach method for arrays:

array.forEach(print);

If you don’t understand the code above then go and read the documentation.

That’s fine for arrays but what about DOM node lists? That’s where most of our loop writing is concentrated. Fortunately, the clever Mozilla developers provide generic array methods to help us:

var lists = document.getElementsByTagName("UL");
Array.forEach(lists, function(list) {
    list.className = "menu";
});

Cool huh? The Array.forEach method treats any object passed to it as if it were an array. So long as that object has a length property then we are OK.

We can easily implement this method for non-Mozilla browsers:

// array-like enumeration
if (!Array.forEach) { // mozilla already supports this
    Array.forEach = function(object, block, context) {
        for (var i = 0; i < object.length; i++) {
            block.call(context, object[i], i, object);
        }
    };
}

I’ve been using this technique for enumeration quite a lot recently and I decided to extend the idea:

// generic enumeration
Function.prototype.forEach = function(object, block, context) {
    for (var key in object) {
        if (typeof this.prototype[key] == "undefined") {
            block.call(context, object[key], key, object);
        }
    }
};


// globally resolve forEach enumeration
var forEach = function(object, block, context) {
    if (object) {
        var resolve = Object; // default
        if (object instanceof Function) {
            // functions have a "length" property
            resolve = Function;
        } else if (object.forEach instanceof Function) {
            // the object implements a custom forEach method so use that
            object.forEach(block, context);
            return;
        } else if (typeof object.length == "number") {
            // the object is array-like
            resolve = Array;
        }
        resolve.forEach(object, block, context);
    }
};

This allows me to write loops without knowing what kind of object I’m dealing with:

function printAll() {
    forEach (arguments, function(object) {
        forEach (object, print);
    });
};
// or
forEach (document.links, function(link) {
    link.className = "super-link";
});
// or
forEach ([1, 2, 3], print);
forEach ({a: 1, b: 2, c: 3}}, print);
// etc

Explanation

The global forEach function allows us to enumerate any object according to it’s type. If the object is array-like (has a length property) then we enumerate it like an array. All other objects are enumerated using the standard for var x in y mechanism.

When enumerating over objects, the discovered keys are compared against Object.prototype. If the key is defined on the Object object then it is not enumerated. That means that you cannot enumerate the built-in methods like toString and valueOf.

The global forEach function will delegate the enumeration of functions to Function.forEach. So, if you choose to enumerate over a Function object you will skip the built-in methods there too.

The Kick-Ass Bit

Although I’ve defined a forEach method on Function.prototype this is never called by the global forEach function (except when you are enumerating functions).

I’ve provided this as a bonus feature. :-) Basically, by calling the forEach method on a function you can enumerate an object and compare the keys with that function’s prototype. That means that you will only enumerate custom properties of the object. An example is required:

// create a class
function Person(name, age) {
    this.name = name || "";
    this.age = age || 0;
};
Person.prototype = new Person;


// instantiate the class
var fred = new Person("Fred", 38);


// add some custom properties
fred.language = "English";
fred.wife = "Wilma";

Enumerate using the standard forEach method:

forEach (fred, print);
// => name: Fred
// => age: 38
// => language: English
// => wife: Wilma

Enumerate using the Person.forEach method:

Person.forEach (fred, print);
// => language: English
// => wife: Wilma

Note that the properties defined on the prototype are not enumerated in the second example.

Conclusion

I’m totally in love with this technique. I now use it all the time. It makes my code more readable and I feel safer enumerating over objects on different platforms.

It also saves on typing. 

Download: forEach.js



Tutorial Pages:
» Enumerating JavaScript Objects


 | Bookmark Print |   Write For Us
Related Tutorials:
» JavaScript Debugging Techniques with Firebug
» Striped Tables Using JavaScript
» Opening PDFs in a New Window with JavaScript
» Essential Javascript -- A Javascript Tutorial
» Submit Forms Conditionally using JavaScript
» How to Setup a Randomising Function



About the NetVisits, Inc Network | Write For Us | Advertise
Copyright ©2007 NetVisits, Inc Network. All Rights Reserved. Privacy Policy.
Visit other NetVisits, Inc. sites: