{"id":817,"date":"2011-09-24T17:52:03","date_gmt":"2011-09-24T17:52:03","guid":{"rendered":"http:\/\/kera.name\/articles\/?p=817"},"modified":"2011-09-24T18:00:21","modified_gmt":"2011-09-24T18:00:21","slug":"a-brief-introduction-to-javascript-objects-arrays","status":"publish","type":"post","link":"https:\/\/kera.name\/articles\/2011\/09\/a-brief-introduction-to-javascript-objects-arrays\/","title":{"rendered":"A Brief Introduction to Javascript Objects &#038; Arrays"},"content":{"rendered":"<p><sub>(<strong>tl;dr:<\/strong> <code>Object<\/code>s are <code>{}<\/code> and <code>Array<\/code>s are <code>[]<\/code>; use string keys and <code>for .. in<\/code> only with <code>Object<\/code>s.)<\/sub><\/p>\n<hr \/>\n<p>Having come up against disbelievers too often now, I&#039;ve decided to take matters into my own hands and definitively conclude this argument once and for all.<\/p>\n<p><em>Arrays may <strong>not<\/strong> have string keys.<\/em><\/p>\n<p>Take the following oft-seen pattern:<\/p>\n<pre><code>var myArray    = new Array();\nmyArray['foo'] = 'lol';\n\/\/ an array with one element?<\/code><\/pre>\n<p>This is <strong>wrong<\/strong>. Broken. Bad, wrong, invalid. Naughty. And constantly misunderstood.<\/p>\n<hr \/>\n<h2>Objects<\/h2>\n<p>The issue stems from both terminology and misleading language design. Pretty much everything in Javascript is an <em>object<\/em>, and those that are not objects &mdash; the primitive types like integers, booleans and strings &mdash; all have object-type counterparts. This is even true for functions!<\/p>\n<p>At its most basic, a Javascript object is a collection of key\/value pairs. We can create our own typeless object instance using <code>{}<\/code> notation (syntax from which the JSON data format is derived, incidentally):<\/p>\n<pre><code>var myObj = {\n   'foo': 1,\n   'bar': 2\n};<\/code><\/pre>\n<p>We could also build up the properties individually, using <code>obj[x]<\/code> notation:<\/p>\n<pre><code>var myObj    = {}; \/\/ `new Object()` would do, too\nmyObj['foo'] = 1;\nmyObj['bar'] = 2;<\/code><\/pre>\n<p>This notation looks like &#034;array&#034; 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 &#034;map&#034; or&#8230; an &#034;associative array&#034;. This is only a <em>general<\/em> term, though, because in Javascript they are just &#034;objects&#034;.<\/p>\n<p>Why does this terminology matter? It matters because Javascript has confused matters by providing functionality for a stricter subset of the &#034;array&#034; concept: the numerically-indexed <code>Array<\/code> type.<\/p>\n<hr \/>\n<h2>Arrays<\/h2>\n<h3>Introduction<\/h3>\n<p>The <code>Array<\/code> type is a specialisation of objects that provides an element access API (<code>.push<\/code> and <code>.length<\/code>) and maintains numeric, sequential keys for you:<\/p>\n<pre><code>var myArray = []; \/\/ `new Array()` would do, too\nmyArray.push('house');\nmyArray.push('bird');<\/code><\/pre>\n<p>Or, again, we can do this in one push:<\/p>\n<pre><code>var myArray = ['house', 'bird'];\n\nfor (var i = 0; i < myArray.length; i++) {\n   console.log(myArray[i]);\n}\n\/\/ Output: \"house\" \"bird\"<\/code><\/pre>\n<p>Notice how you still access the array's elements using the <code>obj[x]<\/code> notation. That's because the <code>Array<\/code> stores its elements as properties in itself, alongside the various other properties that it needs in order to function (like <code>length<\/code> and <code>push<\/code>, plus any other implementation-defined internal properties).<\/p>\n<h3>Looping over <em>properties<\/em> not <em>elements<\/em><\/h3>\n<p>However, this is also why looping through an <code>Array<\/code> with <code>for .. in<\/code> syntax is <em>wrong<\/em> -- <code>for .. in<\/code> loops through low-level <em>object keys<\/em>, not the \"elements\" of this higher-level \"array\" abstraction:<\/p>\n<pre><code>var myArray = ['house', 'bird'];\nfor (var key in myArray) {\n   console.log(myArray[key]);\n}\n\/\/ Possible output: \"bird\" \"concat\" \"every\" \"filter\" \"forEach\"\n\/\/                  \"house\" \"indexOf\" \"join\" \"lastIndexOf\"\n\/\/                  \"length\" \"map\" \"pop\" \"push\" \"reduce\"\n\/\/                  \"reduceRight\" \"reverse\" \"shift\" \"slice\"\n\/\/                  \"some\" \"sort\" \"splice\" \"unshift\"<\/code><\/pre>\n<p>In fact, recent implementations of Javascript handle this more intelligently and will still output just <code>'house' 'bird'<\/code> for the above loop (*), but you cannot and should not rely on this behaviour.<\/p>\n<h3>Array holes<\/h3>\n<p>Another issue is that, due to some magic, setting <code>myArray[5]<\/code> will automatically increase <code>length<\/code> along with it. However, this can cause \"holes\" to \"appear\" if you use object loop syntax:<\/p>\n<pre><code>var myArray = [\"hello\"];\narr[100]    = \"goodbye\";<\/code><\/pre>\n<p>Now <code>myArray<\/code> has a <code>length<\/code> of 100, but we only set two user-defined properties in the underlying object representation. Using <code>for .. in<\/code> will yield two indexes, while the <code>for<\/code> loop will yield 101 indexes, where the 99 new numeric ones have an <code>undefined<\/code> value. Which one did you intend?<\/p>\n<hr \/>\n<h2>The Misunderstanding<\/h2>\n<p>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.<\/p>\n<p>In addition to the <code>for .. in<\/code> trickery mentioned above (*), the order of enumeration even on <code>Array<\/code> objects is the order in which the properties were created:<\/p>\n<pre><code>var array = [];\narray[2] = 'c';\narray[1] = 'b';\narray[0] = 'a';\n\nfor (var p in array) {\n   \/\/ p will be 2, 1 then 0 on IE <= 8\n}<\/code><\/pre>\n<p>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.<\/p>\n<h3>Non-integer keys<\/h3>\n<p>Another oft-seen chunk of code looks like this:<\/p>\n<pre><code>var myArray    = [];\nmyArray[\"foo\"] = 42;\nmyArray[\"bar\"] = 84;\n\nconsole.log(myArray[\"foo\"]);\n\/\/ Output: \"42\"<\/code><\/pre>\n<p>Argh! Now the programmer has created an <code>Array<\/code>, but is using functionality of its <em>underlying existence as an <code>Object<\/code><\/em> instead of the proper API tools. In particular, he or she is setting <em>object properties<\/em>, bypassing the <code>Array<\/code> functionality completely.<\/p>\n<p>It appears to work, because we can still use <code>myArray[x]<\/code> to access object properties: this hasn't changed. But we're completely bypassing the fact that this object is, more specifically, of type <code>Array<\/code>.<\/p>\n<p>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 <code>Array<\/code> prototype, when we loop over object properties we still only see those that we think we set:<\/p>\n<pre><code>var myArray    = [];\nmyArray[\"foo\"] = 42;\nmyArray[\"bar\"] = 84;\n\nfor (var key in myArray) {\n   console.log(myArray[key]);\n}\n\/\/ Output: \"42\" \"84\"<\/code><\/pre>\n<p>But <a href=\"http:\/\/stackoverflow.com\/questions\/242841\/javascript-foreach-vs-for\">it's still not correct<\/a>, which we only actually notice when we use a proper <code>Array<\/code> loop and notice that <code>.length<\/code> has no idea what's going on. And how could it? It's a number.<\/p>\n<pre><code>var myArray    = [];\nmyArray[\"foo\"] = 42;\nmyArray[\"bar\"] = 84;\n\nfor (var i = 0; i < myArray.length; i++) {\n   console.log(myArray[i]);\n}\n\/\/ Output: (nothing)<\/code><\/pre>\n<p>So, **we shouldn't be looping over <code>Array<\/code>s with <code>for .. in<\/code>.<\/p>\n<p>It also doesn't help that many reference materials (<a href=\"http:\/\/i-programmer.info\/programming\/javascript\/1441-javascript-data-structures-the-associative-array.html\">example<\/a>) continue to insist upon using the terms \"array\" and \"object\" interchangeably, and the difference between <code>Array<\/code> and \"associative array\" (i.e. <code>Object<\/code>) is not at all clear from the terminology.<\/p>\n<p>At least <a href=\"https:\/\/developer.mozilla.org\/en\/JavaScript\/Reference\/Global_Objects\/Array\">the Mozilla documentation makes this clear<\/a>:<\/p>\n<blockquote>\n<p>An array is a JavaScript object. Note that you shouldn't use it as an associative array, use Object instead.<\/p>\n<\/blockquote>\n<hr \/>\n<h2>How to do it properly<\/h2>\n<p>It's tempting to rely on the above magical browser behaviours anyway. \"<code>for .. in<\/code> lets us use string keys where <code>for i < .length<\/code> does not, so let's just use <code>for .. in<\/code>, right?\" Wrong.<\/p>\n<p><strong>We shouldn't be using string keys in the first place.<\/strong><\/p>\n<p>So, in conclusion:<\/p>\n<ul>\n<li>\n<p>The correct way to <a href=\"http:\/\/stackoverflow.com\/questions\/500504\/javascript-for-in-with-arrays\">loop through an <code>Array<\/code><\/a> is to use the API that it provides, so that:<\/p>\n<ul>\n<li>You are not mixing up semantics;<\/li>\n<li>You are not relying on implementation details;<\/li>\n<li>You are not prone to unexpected and surprising behaviours;<\/li>\n<li>Your code \"works\" properly on more browsers and in the future;<\/li>\n<\/ul>\n<p>So:<\/p>\n<pre><code>var myArray = ['house', 'bird'];\nfor (var i = 0; i < myArray.length; i++) {\n    console.log(i + \": \" + myArray[i]);\n}\n\/\/ Output: \"0: house\" \"1: bird\"<\/code><\/pre>\n<\/li>\n<li>\n<p>The correct way to use an <code>Array<\/code> is with <em>numeric<\/em> keys.<br \/>\nIf you need an \"array\" with string keys, you may use an \"associative array\": a bog-standard <code>Object<\/code>.<\/p>\n<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Having come up against disbelievers too often now, I&#039;ve decided to take matters into my own hands and definitively conclude this argument once and for all.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[60,20],"_links":{"self":[{"href":"https:\/\/kera.name\/articles\/wp-json\/wp\/v2\/posts\/817"}],"collection":[{"href":"https:\/\/kera.name\/articles\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kera.name\/articles\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kera.name\/articles\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/kera.name\/articles\/wp-json\/wp\/v2\/comments?post=817"}],"version-history":[{"count":26,"href":"https:\/\/kera.name\/articles\/wp-json\/wp\/v2\/posts\/817\/revisions"}],"predecessor-version":[{"id":841,"href":"https:\/\/kera.name\/articles\/wp-json\/wp\/v2\/posts\/817\/revisions\/841"}],"wp:attachment":[{"href":"https:\/\/kera.name\/articles\/wp-json\/wp\/v2\/media?parent=817"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/kera.name\/articles\/wp-json\/wp\/v2\/categories?post=817"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kera.name\/articles\/wp-json\/wp\/v2\/tags?post=817"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}