A common way of instantiating objects in javascript is to use a constructor function. To define properties and methods using a constructor function you might use the 'this' keyword, as seen in the following example:

var Car = function(x,y){
  this.horsepower = x;
  this.topspeed = y;
  this.vroom = function() {
      console.log("Vroooooooom!")
  }
   //don't do this, this is just an
  //illustration of an anti-pattern, keep reading...
}

var ferrari = new Car(600,230);

ferrari.horsePower;   //returns 230
ferrari.topspeed;     //returns 600
ferrari.vroom();      //logs "Vroooooooom!"

The method above works fine, but the drawback is that we're re-creating vroom every time we create a new object. This might, in rare cases, be our desired intention, but in most cases we would prefer to use the more inexpensive approach of adding vroom to the prototype of Car.

Consider this illustration of how .prototype is typically used:

var Car = function(x,y){
  this.horsepower = x;
  this.topspeed = y;
}
Car.prototype.vroom = function(){
  console.log("Vroooooooom!")
}

var ferrari = new Car(600,230);

ferrari.horsePower       //returns 230
ferrari.topspeed         //returns 600
ferrari.vroom()          //logs "Vroooooooom!"

See how Ferrari inherited the 'vroom' method there? On the surface it's pretty easy to tell what happened: we set Car.prototype.vroom to a function, and that function will now be a method of all subsequent cars that get created using the new Car constructor.

The reason why we don't want to just define the vroom method within the body of the Car constructor function like we did with HorsePower and Topspeed, is that we only have to create the method once when we add it to Car's prototype. It no longer needs to be re-created every time we create a Car, and this saves memory. Additionally, we make a distinction between vroom and the other two methods: HorsePower and Topspeed. You might have noticed that the latter two properties change depending on the x and y values that get passed into the Car constructor function when you invoke it, whereas vroom never changes. In this way, you can make multiple car objects with different speeds and horsepowers. Vroom however, will always be the same.

But How Does It Work?

Let's step back for a moment to address the question of, "How exactly does this prototype thing work"?

Functions in Javascript can contain properties, just like objects (because they ARE objects). Everytime you create a function, it automatically comes with a hidden 'prototype' property, that itself contains an empty object. You can add properties to this object if you want, the same way you could add properties to any object, but there's only one reason we would ever want to do so.

Filling up the prototype property of a function serves one main purpose: When you instantiate an object using the 'new' keyword, the new object that gets created will contain all the methods and properties on the constructor function's prototype. That's why in the previous example, Ferrari has the vroom property (along with all the properties defined using 'this'):

//Same code as before
var Car = function(x,y){
  this.horsepower = x;
  this.topspeed = y;
}
Car.prototype.vroom = function(){
  console.log("Vroooooooom!")
}

var ferrari = new Car(600,230);

ferrari.horsePower       //returns 230
ferrari.topspeed         //returns 600
ferrari.vroom()          //logs "Vroooooooom!"

If you're curious to know how exactly the new keyword magically creates a new object that contains all of the constructor function's prototype properties (vroom) as well as the properties defined inside the function body using the 'this' keyword (horsepower and topspeed), a brief, in-depth explanation is required.

I will notate what the new keyword is doing under the hood by enclosing the 'under-the-hood' part in << >>:

  var Car = function(x,y){

      <<this = {vroom: function(){...etc},
                honk: function(){...etc}} >> 
                //see [note1]

      this.Horsepower = x; //see [note2]
      this.Topspeed = y; //see [note2]

      <<return this> //see [note3]
  }
  Car.prototype.vroom = function(){
      console.log("Vrooooooooom!");
  }
  Car.prototype.honk = function(){
      console.log("Beep beep!");
  }
  var Ferrari = new Car(600,230); //see [note4]

*note1: 'this' gets set to a new object that contains (actually it just references, but don't worry about that for now) all of the properties contained within Car.prototype

*note2: Horsepower and Topspeed get added to the new object

*note3: The new object gets returned, and it now has four properties

*note4: Ferrari now looks like this:

//log ferrari
   { 
     horsePower:600,
     topspeed:230,
     vroom: function(){console.log("Vroooooooom!")},
     honk: function(){console.log("Beep beep!")}
    }

That's pretty much it. The only asterisk I'll add to the above explanation is that the resulting object (Ferrari in this instance) does contain the vroom method - in the sense that it can use it, and if you run a 'for in' loop over it, the vroom keyword will get returned - but it doesn't REALLY contain the vroom method because if you type Ferrari.hasOwnProperty('vroom') you will get 'false'. That is because (and this is why i said earlier that using prototypes saves memory), the vroom property is really only being referenced when you invoke Ferrari.vroom(), it actually still only resides in the Car.prototype object.

comments powered by Disqus