Using For In Loops Safely

Many of the JavaScript Frameworks and Libraries used today have the bad habit of attaching methods to the Prototype of Array and Object. While, in theory it would be really nice to be able to extend these Object, in practice, doing so breaks "For … in" loops. For example, if you include Json.js" in your project, you have the power to easily work with JSON objects. However, it attaches "toJSONString" method to "Array.prototype". So when you use the "For … In" loop on any array, one of the keys will be the bogus key "toJSONString".

This is frustrating as "For … In" loops are the only way, in JavaScript, that you can iterate on the keys of an Object and associative Arrays. Most Frameworks that modify these objects also add functions that allow you to work around this limitation, but then you become dependent on them to do any Array iterations. Douglas Crockford (see JSLint) and other JavaScript gurus recommend keeping a list of keys that you want to ignore and always filtering your "For … In" loop with it. This is one way to fix the problem, but what does one do when they dont know the keys or Array/Object prototypes change dynamically as your JavaScript executes, such as when you use YUI to extend Object and it attaches the "constructor" variable to Objects prototype.

Often you need the ability to dynamically determine what keys to filter before each "For … In" loop. To accomplish this, I have developed the following function:

Example 1: GetKeysToIgnore

var getKeysToIgnore = function() {
	var tarr = [], // empty array
		tobj = {}, // empty object
		tdom = document.getElementsByTagName("body"), // empty nodelist
		keys = {item: true, length: true};// default keys to ignore

	// iterate on the native array object
	for (var k in tarr) {keys[k] = true;}

	// iterate on the native object object
	for (var l in tobj) {keys[l] = true;}

	// iterate on the nodelist, but dont ignore indices
	for (var m in tdom) {
		if (isType(parseInt(m, 10), number)) {continue;}
		keys[m] = true;
	return keys;

This method creates an instance of each of the three type of iterable JavaScript objects: Array, Object, NodeList (especially important of Safari). Since, these objects are empty, they contain only the keys for elements that have been attached to their Prototypes. Therefore, when we iterate on it we can create a hash Object of all these keys. The default list is just some keys that you want to reserve for your project, which may or may not always be attached to one of these 3 objects.

Call "getKeysToIgnore" before any "For … In" loop and then filter these keys out:

Example 2: Filtering

var test = {
	key1: 1,
	key2: "1223&rsquot;,
	key3: new Date()

var ikeys = getKeysToIgnore();
for (var key in test) {
	// key is not in our ignore hash
	if (! ikeys[key]) {
		// put your logic here

Using this technique, you could modify the "batch" Function of Core.js to use this technique, thereby supporting both Array and Object iteration:

Example 3: Batch

Core.batch = function(o, fn) {
	var args = Array.prototype.slice.apply(arguments, [2]);
	var ikeys = getKeysToIgnore();
	args.unshift(null, null);

	// iterate on the items, executing the function, and stoping when function returns true or visited all elements
	for (var key in o) {
		// key is not in our ignore hash
		if (! ikeys[key]) {
			args[0] = o[i];
			args[1] = i;
			var rs = fn.apply(this, args);
			if (rs) {
				return rs; // allows the batch function to return a found result

While this is a little more powerful than the Array based "batch" Function, it is a little slower because the ikeys array must be created each time before batching. Something to think about is how to improve/cache getKeysToIgnore better and increase the performance of this Function.