Flavor TextJavascript Object and Array
Phabricator Flavor TextJavascript

Javascript Object and Array Article

This document describes the behaviors of Object and Array in Javascript, and a specific approach to their use which produces basically reasonable language behavior.

Primitives

Javascript has two native datatype primitives, Object and Array. Both are classes, so you can use new to instantiate new objects and arrays:

var a = new Array();    //  Not preferred.
var o = new Object();

However, you should prefer the shorthand notation because it's more concise:

var a = [];             // Preferred.
var o = {};

(A possible exception to this rule is if you want to use the allocation behavior of the Array constructor, but you almost certainly don't.)

The language relationship between Object and Array is somewhat tricky. Object and Array are both classes, but "object" is also a primitive type. Object is also the base class of all classes.

typeof Object;        // "function"
typeof Array;         // "function"
typeof {};            // "object"
typeof [];            // "object"

var a = [], o = {};
o instanceof Object;  // true
o instanceof Array;   // false
a instanceof Object;  // true
a instanceof Array;   // true

Objects are Maps, Arrays are Lists

PHP has a single array datatype which behaves like as both map and a list, and a common mistake is to treat Javascript arrays (or objects) in the same way. Don't do this. It sort of works until it doesn't. Instead, learn how Javascript's native datatypes work and use them properly.

In Javascript, you should think of Objects as maps ("dictionaries") and Arrays as lists ("vectors").

You store keys-value pairs in a map, and store ordered values in a list. So, store key-value pairs in Objects.

var o = {               //  Good, an object is a map.
  name: 'Hubert',
  species: 'zebra'
};

console.log(o.name);

...and store ordered values in Arrays.

var a = [1, 2, 3];      //  Good, an array is a list.
a.push(4);

Don't store key-value pairs in Arrays and don't expect Objects to be ordered.

var a = [];
a['name'] = 'Hubert';   //  No! Don't do this!

This technically works because Arrays are Objects and you think everything is fine and dandy, but it won't do what you want and will burn you.

Iterating over Maps and Lists

Iterate over a map like this:

for (var k in object) {
  f(object[k]);
}
NOTE: There's some hasOwnProperty nonsense being omitted here, see below.

Iterate over a list like this:

for (var ii = 0; ii < list.length; ii++) {
  f(list[ii]);
}
NOTE: There's some sparse array nonsense being omitted here, see below.

If you try to use for (var k in ...) syntax to iterate over an Array, you'll pick up a whole pile of keys you didn't intend to and it won't work. If you try to use for (var ii = 0; ...) syntax to iterate over an Object, it won't work at all.

If you consistently treat Arrays as lists and Objects as maps and use the corresponding iterators, everything will pretty much always work in a reasonable way.

hasOwnProperty()

An issue with this model is that if you write stuff to Object.prototype, it will show up every time you use enumeration for:

var o = {};
Object.prototype.duck = "quack";
for (var k in o) {
  console.log(o[k]); // Logs "quack"
}

There are two ways to avoid this:

  • test that k exists on o by calling o.hasOwnProperty(k) in every single loop everywhere in your program and only use libraries which also do this and never forget to do it ever; or
  • don't write to Object.prototype.

Of these, the first option is terrible garbage. Go with the second option.

Sparse Arrays

Another wrench in this mess is that Arrays aren't precisely like lists, because they do have indexes and may be sparse:

var a = [];
a[2] = 1;
console.log(a);       // [undefined, undefined, 1]

The correct way to deal with this is:

for (var ii = 0; ii < list.length; ii++) {
 if (list[ii] == undefined) {
   continue;
 }
 f(list[ii]);

}

Avoid sparse arrays if possible.

Ordered Maps

If you need an ordered map, you need to have a map for key-value associations and a list for key order. Don't try to build an ordered map using one Object or one Array. This generally applies for other complicated datatypes, as well; you need to build them out of more than one primitive.