Understanding Javascript Objects

I have been reading the book Javascript Patterns, which is fairly easy reading and I would recommend it if you wish to understand Javascript a little better.

This post will describe JS objects, which is something you'll come across when you're writing almost any kind of JS.

Objects in Javascript are essentially hash tables of key value pairs. The values can be almost anything; strings, functions, other objects, etc.

It is worth noting that these objects are mutable which can offer some cool features but obviously has it's drawbacks. This is why understanding scope is very important because otherwise you can get some unexpected behaviour when your object gets changed by something at runtime.

Here's a basic example of this mutability, where we define an empty object and then add a property.

var person = {};
person.name = "Chris James";

Object literals

Objects can be defined upfront like this with a JSON like syntax:

var person = {
    name: "Chris James",
    sayHello: function(){
        console.log("Hi, my name is " + this.name);
    }
};

person.sayHello()

Constructor functions

Although there are no classes in Javascript you can create constructor functions to make objects in a standardised way which makes sense for your application.

var Person = function(name) {
    this.name = name;
    this.sayHello = function() {
        console.log("Hi, my name is " + this.name);
    };
}

var chris = new Person("Chris James");
chris.sayHello();

New

Using new will implicitly return the this object that you create in the function. You dont need to do var this = {}; - that is also done for you.

In other words what happens is similar to the first example in this post where it makes a new empty object and then adds the properties defined in the function.

Most importantly, the newly created object will also inherit the prototype of the function you defined.

Prototypes?

Javascript uses prototypal inheritance. A prototype simply defines methods that an object referencing it can run.

When you try to run a method of an object, if it cannot find the method locally to the object it will then try to find it in the object's prototype.

Like most things in Javascript, prototypes are mutable which can offer some interesting (and insane) power to a programmer. You could have a bunch of widgets on a web page and based on an event, such as a user action change the functionality of all of them by changing the definition of their prototype.

Returning to the Person constructor function before, I defined a function called sayHello. Every time this constructor function is called and an object is made, a copy of that function will be made inside that object. This is inefficient and should be refactored to use a prototype instead.

This will potentially make unit testing easier too as you can simply create a mock object which has the prototype you wish to test.

var Person = function(name) {
    this.name = name;
}

var chris = new Person("Chris James");

chris.sayHello(); // will fail

//The following is defined after "chris" is made
Person.prototype.sayHello = function(){
    console.log("Hi, my name is " + this.name);
};

//But chris will have access to it by virtue of it's prototype
chris.sayHello();

//Prototypes are mutable...
Person.prototype.sayHello = function(){
    console.log("Hola, me llamo " + this.name);
};

chris.sayHello(); // Spanish!

Therefore, if you think your object will be created more than once and has methods you should definitely consider including the functionality in a prototype to save memory.

Dont forget new

Unfortunately forgetting new will not cause any obvious errors. What it will do is bind new to the application's Global object, rather than the "new" this which would normally be implicitly returned. In a browser, this would be the window object; fail!

This behaviour is fixed in the strict mode of ECMAScript 5 but this is not always available so it's better to be safe than sorry.

Coffeescript constructor functions

If you use Coffeescript's class syntax, it will take care of prototyping methods for you:

class Person
   constructor: (@name) ->
   sayHello: -> console.log("Hello #{@name}")

chris = new Person("chris")
chris.sayHello()

Results in

var Person, chris;

Person = (function() {

  function Person(name) {
    this.name = name;
  }

  Person.prototype.sayHello = function() {
    return console.log("Hello " + this.name);
  };

  return Person;

})();

chris = new Person("chris");

chris.sayHello();

However, as far as I can tell it wont protect you against not using new

Constructor functions vs Object literals

So it seems there are two ways to create objects but which should you use?

If you wish for an object to just contain data then you should use object literals. It is also acceptable to use object literals if you are sure that you wont make another "instance" of your object to have behaviours attached to it.

If you feel you will have more than one instance of your object and in particular if you wish to add behaviour and alter it across all instances then create it using a constructor function.