Frontend Mayhem

JavaScript: Arrays vs Objects

September 05, 2016

When learning JavaScript, we all stumble across the phrase “Arrays are just Objects in JavaScript”. Today, we’re going to put that statement under the microscope.

investigate

let groceries = ["apples"]
console.log(typeof groceries) // Logs "object"

Looking at the code it is quite obvious that an array is of type object. But, what does that mean?

Follow this link to read about the typeof operator, if you aren’t familiar with it.

Inheritance

In order to understand the difference between objects & arrays, let’s take a quick peek at inheritance in JavaScript. Prototypal inheritance is a big topic on its own & warrants a separate blog post. However, for the purpose of this post, I’m going to keep things extremely basic.

Every object in JavaScript holds a reference to its parent (prototype) object. When a method is called, JavaScript will look for it on the object you’re working with. If not found, it will look at the prototype. It will keep going down the prototype chain until the property is found, or it reaches the root Object.

var person = {
  name: "Mr. Frontend Mayhem",
}

//toString method comes from the prototype because it's not defined on the person object.
console.log(person.toString()) // Logs "[object Object]"

person.toString = function() {
  return this.name
}

//person object has a toString method of it's own now, which will be used.
console.log(person.toString()) // Logs "Mr. Frontend Mayhem"

In the example above, a person object is created with an own property called name. When the toString method is called, the person object is first checked, followed by a lookup on its prototype i.e. Object.prototype. The implementation provided by the prototype is used, which has a default behavior of returning [object Object].

Next, toString method is created on the person object itself, which will be used when toString is called from that point on.

Difference between Objects & Arrays

Despite just being objects under the hood, arrays behave very differently from regular objects. The reason is the Array.prototype object, which has all the Array specific methods. Every new array inherits these extra methods from Array.prototype.

Key thing to note is that the value of the prototype property of Array.prototype is Object.prototype. This means two things:

  1. Arrays are just objects but with some extra methods.
  2. There is nothing an object can do that an array can’t.

Let’s take a look at this in action.

let grocery = ["apple"]
// Prototype of an array is "[]"
console.log(grocery.__proto__) // Logs []
// Looking down the chain, prototype is "{}"
console.log(grocery.__proto__.__proto__) // Logs {}

// Arrays are instance of both Array & Object'
console.log(grocery instanceof Array) // Logs true
console.log(grocery instanceof Object) // Logs true
// Objects aren't instance of array
console.log({} instanceof Array) // Logs false

// Array.prototype contains Array specific methods such as .push
console.log(typeof Array.prototype.push) // Logs function

// Returns undefined because Object
// prototype doesn't have the push method
console.log(typeof Object.prototype.push) // Logs undefined

Oddities

Like everything in JavaScript, Arrays come with their own set of peculiarities.

Non-indexed Properties
Since arrays are just objects in disguise, it is possible to set non-indexed properties on them. This is usually the first thing that catches people by surprise. In the example below, I’ve set two non-indexed properties named sorted & authored by on the groceries array. Note: both dot & bracket notation is supported, just like objects.

var groceries = ["banana", "apple"]

groceries.sorted = false
groceries["authored by"] = "frontendmayhem"

//Non-indexed properties won't get logged
console.log(groceries) // Logs ["banana","apple"]

//But looking at the keys that exist on the groceries array
// it is clear that both of those properties were added
// to the array.
console.log(Object.keys(groceries))
// Logs ["0","1","sorted","authored by"]

//non-indexed properties don't affect the length of the array
console.log(groceries.length) // Logs 2

//length is not "count" of items
groceries[9] = "chicken"
console.log(groceries.length) // Logs 10

length
length property of an array is another one which often becomes a source of confusion. It is often misinterpreted as the count of items in the array. However, that isn’t the case. The value of length is numerically greater than the biggest array index. Due to this behaviour, non-indexed properties don’t affect the length of the array, as demonstrated in the code sample above.

Another scenario where length can cause some confusion is when an item is added at an index which is higher than the current length of the array. Notice that in the code sample above, the length of the array jumped from 2 to 10 after I added a third item to the array at the index 9.

Whenever the value of the length property is changed, every element whose index is larger than the new length is deleted.

In order to get the correct length value, you could use Object.keys(groceries).length. Keep in mind, this will include non-indexed properties as well, unless you define them as non-enumerable. Eg:Object.defineProperty(groceries, "sorted", { value: false, enumerable: false, configurable: true, writable: true });

What should I use?

A general rule of thumb that I try to follow is that if you need to store a collection of properties of varying types, use an object. Otherwise, use an array.


Watandeep Sekhon

Written by Watandeep Sekhon.
Website / Twitter / LinkedIn / Instagram