On objects, classes and prototypes in JavaScript

When developers from other languages start to use JavaScript more frequently one of the biggest stumbling blocks is trying to apply classical OO methods to JavaScript development. I’d like to go through the different options available and try clear up some of the confusion surrounding objects, classes and prototypes – once and for all.

Object Literals

These objects are probably the most frequently used because of their ease of creation and use. They are a way of storing or passing a group of values and methods together in one convenient entity. The syntax is as follows:

var me = {
  name: 'james', 
  age: 37, 
  isOld: function() {
    return this.age < 40
  }
};
alert(me.isOld()); //alerts false

Underneath, objects are just a hash-table, with a key and value. It is perfectly valid syntax to refer to members in an object using array syntax eg:

alert(me['name']); //alerts 'james'

Objects are also mutable in JavaScript which simply means you can add members to them after they have been defined:

me.greet = function() {
  return 'Hello ' + this.name;
}; //returns 'Hello james'

Another function greet was added to the object. We use the this keyword to refer to the object from inside one of the object’s own methods.

Constructor functions

What happens however when you want to store information about more than one person? In classical OO, this is where classes come in – you define a ‘template’ which can be used to create a object of a certain type with all the methods and properties filled in ready to be used.

JavaScript is a functional language, so it makes sense perhaps that the solution comes in the form of a type of function called a constructor function. A constructor function works by combining the new keyword with a function used to create a new object every time it is called.

Lets create a Person constructor function:

function Person(name, age) {
  this.name = name;
  this.age = age;
  this.isOld = function() {
    return this.age < 30
  }
}

In the example, Person looks like a normal function – because it is. Note that by convention constructor function names begin with a capital letter. The constructor is called when an object is created using the new keyword. Lets create two new objects:

var me = new Person('james', 37);
var you = new Person('visitor', 22);
alert(me.isOld()); //alerts true
alert(you.isOld()); // alerts false

We can use the Person constructor function to create as many people objects as we need.

Prototypes

In classic OO languages, objects tend to inherit from a base class, usually named object, which then provides a base level of useful functionality. JavaScript has a similar concept, but because it is classless each object has an underlying object instance instead. This is called the prototype. The prototype object contains all the base methods and properties in one place only, and only when these are updated are the members set at the top level of the inheritance chain.

Looking at our example above, we should move the isOld function to the prototype of Person as this means it is referenced in one place only. The code becomes slightly less readable as follows:

function Person(name, age) {
  this.name = name;
  this.age = age;
};
Person.prototype.isOld = function() {
  return this.age < 30
};

In this way, we split the constructor function and the prototype’s functions.

Prototypes and inheritance

What if we wanted to create a more specialised version of a Person – perhaps an Employee to inherit from Person. This is where JavaScript’s prototypal inheritance comes to the fore. WE inherit by setting a constructor function’s prototype before creating an object. This is best explained by example – lets first create an Employee constructor function:

function Employee(name, age, salary) {
  this.salary = salary;
}

We have added a salary property to Employee, now to add the magic sauce that makes an Employee ‘inherit’ from Person:

function Employee(name, age, salary) {
  Employee.prototype.call(this, name, age);
  this.salary = salary;
}
Employee.prototype = new Person();

Two things are happening here. Firstly the prototype. We provide the Employee constructor function with a single instance of Person, created with the new keyword, but without any parameters. When a method or property is not found on Employee the inheritance chain – in this case Person – is checked for the member requested and if it exists, is returned as if it were a member of Employee. Importantly, when a member value is set, the value is set directly on the target object, so that any underlying members are not overwritten. Potentially, this makes prototypal inheritance very efficient.

Secondly, we need a way of passing the age and salary parameters the Person constructor function. This is done by manually calling the prototype’s constructor with the correct context using the call function.

Inconsistencies within the prototypal inheritance model

If you step the code, or if you are eagle eyed, you’ll notice a potential problem with parameters and constructor functions. The Person constructor is called twice. Once when the prototype object is created, and once more when we actually want to call the constructor with parameters for a particular instance. Is this a design flaw, or should we adjust our way of thinking when using prototypal inheritance? I would argue for the latter, given that methods should be (mostly) defined in the prototype and – in our example at least – the name and age properties will continue to be undefined as they were before.

My biggest gripe with prototypal inheritance is not the implementation – though Douglas Crockford would argue that there is little need for a classical model in JavaScript at all.

I would however recommend a cleaner, more organised syntax for organising both the constructor and prototype code based around a function I’ve created called type


var Person = type({
  _constructor: function(name, age) {
    this.name = name;
    this.age = age;
  },
  _prototype: function() {
    this.isOld = function() {
      return this.age < 30;
    }
  };
})

var Employee = type(Person, {
  _constructor: function() {
    this.prototype.call(this, name, age);
    this.salary = salary;
  }
});

The type function creates a necessary layer of abstraction around the process. It combines the clean and simple brevity of an object literal, with the power to define a constructor and prototype all in one location, without having to worry about the semantics of prototypal inheritance later in the code. The code to enable this functionality is also neat and concise:

window.type = function(o, c) {
  if (arguments.length === 1) c = o, o = null;
  if (o) F.prototype = new o();
  return F;
		
  function F(){
    if (c._constructor) c._constructor.apply(this, arguments);
    if (c._prototype) c._prototype.call(F.prototype);
  }
}

On objects, classes and prototypes in JavaScript

2 thoughts on “On objects, classes and prototypes in JavaScript

  1. fabianosoriani says:

    Hi!
    Nice article.
    In the last code block, I’m just confused as it does return on line 4, whilst there is code to be executed below, is there a revised edition of the code, or just me missing something obvious?

Leave a Reply

Your email address will not be published. Required fields are marked *