(tl;dr: Objects are {} and Arrays are []; use string keys and for .. in only with Objects.)


Having come up against disbelievers too often now, I've decided to take matters into my own hands and definitively conclude this argument once and for all.

Arrays may not have string keys.

Take the following oft-seen pattern:

var myArray    = new Array();
myArray['foo'] = 'lol';
// an array with one element?

This is wrong. Broken. Bad, wrong, invalid. Naughty. And constantly misunderstood.


Objects

The issue stems from both terminology and misleading language design. Pretty much everything in Javascript is an object, and those that are not objects — the primitive types like integers, booleans and strings — all have object-type counterparts. This is even true for functions!

At its most basic, a Javascript object is a collection of key/value pairs. We can create our own typeless object instance using {} notation (syntax from which the JSON data format is derived, incidentally):

var myObj = {
   'foo': 1,
   'bar': 2
};

We could also build up the properties individually, using obj[x] notation:

var myObj    = {}; // `new Object()` would do, too
myObj['foo'] = 1;
myObj['bar'] = 2;

This notation looks like "array" access notation due to the influence of other major programming languages and, in fact, this basic key/value functionality is more generally known as a "map" or… an "associative array". This is only a general term, though, because in Javascript they are just "objects".

Why does this terminology matter? It matters because Javascript has confused matters by providing functionality for a stricter subset of the "array" concept: the numerically-indexed Array type.


Arrays

Introduction

The Array type is a specialisation of objects that provides an element access API (.push and .length) and maintains numeric, sequential keys for you:

var myArray = []; // `new Array()` would do, too
myArray.push('house');
myArray.push('bird');

Or, again, we can do this in one push:

var myArray = ['house', 'bird'];

for (var i = 0; i < myArray.length; i++) {
   console.log(myArray[i]);
}
// Output: "house" "bird"

Notice how you still access the array's elements using the obj[x] notation. That's because the Array stores its elements as properties in itself, alongside the various other properties that it needs in order to function (like length and push, plus any other implementation-defined internal properties).

Looping over properties not elements

However, this is also why looping through an Array with for .. in syntax is wrong -- for .. in loops through low-level object keys, not the "elements" of this higher-level "array" abstraction:

var myArray = ['house', 'bird'];
for (var key in myArray) {
   console.log(myArray[key]);
}
// Possible output: "bird" "concat" "every" "filter" "forEach"
//                  "house" "indexOf" "join" "lastIndexOf"
//                  "length" "map" "pop" "push" "reduce"
//                  "reduceRight" "reverse" "shift" "slice"
//                  "some" "sort" "splice" "unshift"

In fact, recent implementations of Javascript handle this more intelligently and will still output just 'house' 'bird' for the above loop (*), but you cannot and should not rely on this behaviour.

Array holes

Another issue is that, due to some magic, setting myArray[5] will automatically increase length along with it. However, this can cause "holes" to "appear" if you use object loop syntax:

var myArray = ["hello"];
arr[100]    = "goodbye";

Now myArray has a length of 100, but we only set two user-defined properties in the underlying object representation. Using for .. in will yield two indexes, while the for loop will yield 101 indexes, where the 99 new numeric ones have an undefined value. Which one did you intend?


The Misunderstanding

Unfortunately, browser vendors aren't helping to stem the spread of the myth that objects and arrays can or should be used in the same way.

In addition to the for .. in trickery mentioned above (*), the order of enumeration even on Array objects is the order in which the properties were created:

var array = [];
array[2] = 'c';
array[1] = 'b';
array[0] = 'a';

for (var p in array) {
   // p will be 2, 1 then 0 on IE <= 8
}

This behaviour makes it quite obvious that the usage isn't quite right, but since Internet Explorer 9 the browser will enumerate the keys in ascending order, only serving to help propagate the myth that this is the right way to do things.

Non-integer keys

Another oft-seen chunk of code looks like this:

var myArray    = [];
myArray["foo"] = 42;
myArray["bar"] = 84;

console.log(myArray["foo"]);
// Output: "42"

Argh! Now the programmer has created an Array, but is using functionality of its underlying existence as an Object instead of the proper API tools. In particular, he or she is setting object properties, bypassing the Array functionality completely.

It appears to work, because we can still use myArray[x] to access object properties: this hasn't changed. But we're completely bypassing the fact that this object is, more specifically, of type Array.

Now, because the above-described "intelligent" behaviour of some browsers works by explicitly ignoring only those object properties that it knows to be properties of the Array prototype, when we loop over object properties we still only see those that we think we set:

var myArray    = [];
myArray["foo"] = 42;
myArray["bar"] = 84;

for (var key in myArray) {
   console.log(myArray[key]);
}
// Output: "42" "84"

But it's still not correct, which we only actually notice when we use a proper Array loop and notice that .length has no idea what's going on. And how could it? It's a number.

var myArray    = [];
myArray["foo"] = 42;
myArray["bar"] = 84;

for (var i = 0; i < myArray.length; i++) {
   console.log(myArray[i]);
}
// Output: (nothing)

So, **we shouldn't be looping over Arrays with for .. in.

It also doesn't help that many reference materials (example) continue to insist upon using the terms "array" and "object" interchangeably, and the difference between Array and "associative array" (i.e. Object) is not at all clear from the terminology.

At least the Mozilla documentation makes this clear:

An array is a JavaScript object. Note that you shouldn't use it as an associative array, use Object instead.


How to do it properly

It's tempting to rely on the above magical browser behaviours anyway. "for .. in lets us use string keys where for i < .length does not, so let's just use for .. in, right?" Wrong.

We shouldn't be using string keys in the first place.

So, in conclusion:

  • The correct way to loop through an Array is to use the API that it provides, so that:

    • You are not mixing up semantics;
    • You are not relying on implementation details;
    • You are not prone to unexpected and surprising behaviours;
    • Your code "works" properly on more browsers and in the future;

    So:

    var myArray = ['house', 'bird'];
    for (var i = 0; i < myArray.length; i++) {
        console.log(i + ": " + myArray[i]);
    }
    // Output: "0: house" "1: bird"
  • The correct way to use an Array is with numeric keys.
    If you need an "array" with string keys, you may use an "associative array": a bog-standard Object.