Matt Sweetman

Latest articles

How JavaScript constructors work

The following is a step-by-step description of what happens behind the scenes when using constructor functions in JavaScript. It also covers setting up prototype chains.

Step 1: Define a constructor Function

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

Internally this is a 3-step process:

  1. Create a Function object and assign it to the variable Animal
  2. Animal.prototype is automatically created and is a regular Object. This becomes the 'blueprint' for the __proto__ object when the function is instantiated. Remember, __proto__ is a non-standard way of accessing the prototype of an instantiated object, technically the prototype should not directly accessible.
  3. Animal.prototype.constructor is automatically created and is a reference pointing back to the Animal function created in step 1.1 (therefore Animal === Animal.prototype.constructor will equal true). This has no practical use internally, but can be used by you later to find the constructor function that instantiated the object.

Step 2: Add some custom properties to the prototype object

Commonly thought of as "inherited" properties when instantiating the constructor function. They really just exist on the prototype object and are retrieved when accessed via the prototype chain.

Animal.prototype.age = 0;
Animal.prototype.move = function() {};

It doesn't really matter what these properties are called for purposes of this demonstration.

Note: Animal.prototype was automatically created in step 1.2

Step 3: Instantiate a new instance

var giraffe = new Animal('henry');

Internally this is a 5 step process:

  1. Create a new regular Object, let's call it T
  2. Set T.__proto__ to point to Animal.prototype, this is the blueprint process described in step 1.2
  3. Set T.constructor to point to Animal.prototype.constructor
  4. Call the Animal constructor function in the context of T (meaning the keyword this inside Animal() is scoped to T)
  5. Return T and assign it to giraffe

Another way of visualizing this:

function createAnimal() {
  var T = {};
  T.__proto__ = Animal.prototype;
  T.constructor = Animal.prototype.constructor;
  Animal.apply(T, arguments);
  return T;
}

Remember you can't really set __proto__ directly, this is just a demonstration of the type of code going on behind the scenes.

Here's a diagram of the object structure so far:

giraffe (the T object)
   ├─name // 'henry'
   └─__proto__ // Animal.prototype object
         ├─age
         └─move

Step 4: Create a subclass of Animal

var Dog = function(name) {
  Animal.prototype.constructor.call(this, name);
};

Here we define a constructor Function, just like we did for Animal in step 1

Note: The original Animal constructor sets the name property, so ideally we want to re-use it. In order to do this we must call the Animal constructor function and pass name to it. Javascript doesn't have the concept of a 'super' property that we can use to access objects in the prototype chain, but we can use the constructor property assigned in step 1.3:

Step 5: Set up Dog's prototype chain so it points to Animal

Dog.prototype = new Animal();

This sets Dog.prototype to a new instance of Animal. If you look back to the process described in step 3 you'll see that Dog.prototype ends up pointing to a new Object whose internal __proto__ points to Animal.prototype. This is a crucial part of the process and worth understanding before moving on.

Note: Contrary to what you might think we don't set Dog.prototype = Animal.prototype, doing this would mean that any properties we added to Dog.prototype would also be added to Animal.prototype, polluting all objects that descend from Animal.

Here's a diagram of the object structure so far:

Dog // constructor Function
 └─prototype // Object returned by the process of instantiating Animal via step 3
       └─__proto__ // Animal.prototype object
             ├─age
             └─move

Step 5: Add properties to Dog.prototype

Dog.prototype.legs = 4;
Dog.prototype.bark = function() { console.log('woof'); };
Dog.prototype.constructor = Dog;

We're actually adding properties onto the object returned via step 3 when we called Dog.prototype = new Animal(); Again this is a crucial part of the process, and shows how all subsequent instances of Dog will share the same prototype object.

We also explicitly set the constructor property, as it would have been wiped out by the reassignment of the prototype in step 5. We are essentially re-doing the work that was already done automatically in step 1.3.

Here's the object structure now:

Dog // constructor Function
 └─prototype // Object returned by the process of instantiating Animal
       ├─legs
       ├─bark
       └─__proto__ // Animal.prototype object
             ├─age
             └─move

Step 6: Create an instance of the Dog object

var husky = new Dog('max');

This is the same process as when we instantiated giraffe in step 3.

Here's the finished object structure:

husky // Object returned by the process of instantiating Dog
 ├─name // 'max'
 └─__proto__ // Dog.prototype object
       ├─legs
       ├─bark
       └─__proto__ // Animal.prototype object
             ├─age
             └─move

Bonus step: Change the object returned from a constructor

If you explicitly return an object from the constructor function then javascript completely ignores everything done in step 3 and simply uses the object you return.

var Cat = function() {
  return new Dog();
};
var tabby = new Cat();
tabby.bark(); // 'woof' - I'm a dog!