JavaScript

Javascript: Understanding Objects vs Arrays (2 of 2)

In Part 1 of this blog post I omitted one very important detail about the nature of Arrays.


Filed under:

In Part 1 of this blog post I omitted one very important detail about the nature of Arrays. You might have heard this already: "Everything is an Object in javascript!". This is technically true. While String, Bool and Number are literals, javascript silently creates a wrapper Object around the primitive at the time of access. There are couple of other special data types that are not Objects (i.e. Undefined and Null), but for the most part you will be dealing with "things" that inherit from the Object prototype. Arrays and Functions also inherit from Object via prototype chain. As the matter of a fact, functions are first class Objects and can be passed as arguments to other functions or even be returned by another function. If you haven't worked with other lambda-like languages, anonymous functions and functions as first class citizens might look weird at a first glance. However, this is a very powerful concept and one of aspects of the language that makes javascript as powerful and fun to work with. But enough about functions, I will leave that topic for another time. What you need to keep in mind is that Array is inheriting from Object. I will explain why this matters in a minute.

This brings us to:

Iteration

Array(s):

If you are coming from languages like Java and PHP, you might want to do something like this:

  var myArray = [0, 1, 1, 2, 3, 5, 8, 13, 22]; // for my OCD friends :)
  var sum = 0;
 
  // Don't do this at home!
  for (var i in myArray) {
    sum += myArray[i];
  }
  console.log(sum); // prints 55

This resembles for-each loop in PHP/Java and I have seen it used to iterate arrays in javascript way too often. What is even more frustrating is that the example above appears to be working, until it isn't. As mentioned above, Arrays inherit from Object via prototype chain. This becomes important when iterating arrays as we can see in the slightly modified example:

  // Augment Object prototype with a new shared property 'foo'
  Object.prototype.foo = "bar";
 
  var myArray = [0, 1, 1, 2, 3, 5, 8, 13, 22]; // for my OCD friends :)
  var sum = 0;
 
  // Don't do this at home!
  for (var i in myArray) {
    sum += myArray[i];
  }
  console.log(sum); // prints 55bar

So what exactly happened here? We never added "bar" to myArray, so why in the world would it show up in sum?
For-in loop in javascript iterates through any enumerable property on the instance and its prototype chain. In this case, for-in loop will iterate through each index in the array, then move onto its prototype (i.e. Object) which has enumerable property 'foo'. There are ways to mitigate this, but instead of fighting our tools we should use standard for loop:

  // Augment Object prototype with a new shared property 'foo'
  Object.prototype.foo = "bar";
 
  var myArray = [0, 1, 1, 2, 3, 5, 8, 13, 22]; // for my OCD friends :)
  var sum = 0;
 
  // Each Array has non-enumerable property 'length' that returns lastIndex + 1
  // Note: This doesn't necessarily imply that there are Array.length elements in the array.
  // For example:
  // var testArr = [];
  // testArr[50] = 5;
  // testArr value is [undefined × 50, 5] and testArr.length will be 51
  // This is something to keep in mind as well.
  for (var i = 0; i < myArray.length; ++i) {
    sum += myArray[i];
  }
  console.log(sum); // Prints 55 - Woohoo!

Object(s):

For looping through object properties on the other hand, for-in is the way to go.

  var parsed = "";
  var myObject = {
    firstname: "Jane",
    lastname: "Doe",
    email: "jdoe@email.com"
  };
 
  for (var prop in myObject) {
    parsed += prop + ": " + myObject[prop] + "\n";
  }
 
  // prints
  // firstname: Jane
  // lastname: Doe
  // email: jdoe@email.com
  console.log(parsed);

It is important to note that above output was in order by accident. Unlike indexed Arrays, Object properties are iterated in arbitrary order and you shouldn't rely on them being in any particular order. Another thing to be aware of is that the loop will go through prototype chain and iterate through any other enumerable properties. I have found this to generally be a desired behavior as for the most part we use prototype chain inheritance to extend and/or decorate current Object. In case where you are interested only in properties for this instance only, you can do additional check within the loop:

  Object.prototype.bad = "stuff";
  var parsed = "";
  var myObject = {
    firstname: "Jane",
    lastname: "Doe",
    email: "jdoe@email.com"
  };
 
  for (var prop in myObject) {
    if (!myObject.hasOwnProperty(prop)) { continue; }
    parsed += prop + ": " + myObject[prop] + "\n";
  }
 
  // prints
  // firstname: Jane
  // lastname: Doe
  // email: jdoe@email.com
  console.log(parsed);

To summarize, Arrays are really good at storing ordered lists and ordering things while the cost of removing/splicing elements is a bit higher. Objects are great at representing schemas and the cost of augmenting or deleting properties is very low, but are not good for preserving order. When working with JSON API endpoints that return list of items, you will generally be working with Arrays of Objects.

I hope you find this article helpful and if you have any questions or comments, I would love to hear from you.

Similar posts

Get notified on new marketing insights

Be the first to know about new B2B SaaS Marketing insights to build or refine your marketing function with the tools and knowledge of today’s industry.