Friday, February 9, 2007

For-In Issues

I came across a little idiosyncrasy yesterday in JavaScript. Perhaps it still shows my ignorance to the nuances of the language, but I hadn't really thought about.

Until I got bit by it.

Look at the following snipit:

var listOfValues = new Array(1, 2, 3, 4, 5);
for(var x in listOfValues) {
doTheThingWithTheDeal(x);
}
It seems simple and straight forward enough. Handy to use to: add some elements to an array and then use a language-level feature to evaluate them. Heck, it so useful they put it into Java.

That's great, but like Java, developers don't always want to be re-inventing the wheel with every application. Still not a problem when one is writing their own libraries. There are, however, a dearth of libraries available for use these days. With the rise of AJAX, we have people building fancy libraries to ease cross-browser development.

One of the most commonly used libraries is the Prototype library. This library provides a number of structures to improve and ease the coding of OO JavaScript. It also extends some of the native JavaScript objects, in order to add some nice methods which the developers of Prototype thought where missing from the implementation.

This is where we start to run into trouble. Array is one of these objects that is
modified with all sorts of convenient methods like indexOf, first, and each. This brings us back to that code snippet. If we now iterate over that array in the example not only will we doTheThingWithTheDeal on the integers in the array, but we'll also doTheThingWithTheDeal on all of those handy-dandy new array methods.

Prototype's (as well as any other library developer who modifies the native objects) response is to not use the for-in control. I would argue that this is a poor extension, since it forces project developers to change their own code. Some structures may rely on this control structure (hence why I'm writing about this).

Why Does It Fail (and what to do about it)

The reason this failure happens is that the properties of all JavaScript objects have attributes. These attributes consist of ReadOnly, DontEnum, DontDelete[1]. The attributes are only available for modification inside the script engine (see the Rhino JS engine, for example).

DontEnum is the attribute that controls whether or not the property shows up during the execution of a for-in structure. For-in operates over all properties of an object. For arrays, this includes the indexed properties, as well as the named properties (In fact, this is true of all objects in JavaScript, due to the languages syntax).

Current implementation of the language doesn't allow this to be modified by users.

Looking at the future of JavaScript, keywords may be added in order to modify the enumerability of properties. Until then, we'll have to either modify our code to work with Prototype, or use libraries that don't extend the native objects.

[1] See the ECMAScript Spec for more information on these attributes.

0 comments: