@title Javascript Object and Array
@group flavortext

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:

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

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

  lang=js
  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.

  lang=js
  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.

  COUNTEREXAMPLE
  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:

  lang=js
  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:

  lang=js
  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##:

  COUNTEREXAMPLE
  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.

