Understanding prototypal inheritance in JavaScript

Dasha Salo picture Dasha Salo · May 21, 2009 · Viewed 46.3k times · Source

I am new to JavaScript OOP. Can you please explain the difference between the following blocks of code? I tested and both blocks work. What's the best practice and why?

First block:

Second block:

function Car(name){
    this.Name = name;
    this.Drive = function(){ 
        console.log("My name is " + this.Name + " and I'm driving.");
    }
}

SuperCar.prototype = new Car();

function SuperCar(name){
    Car.call(this, name);
    this.Fly = function(){
        console.log("My name is " + this.Name + " and I'm flying!");
    }
}

var myCar = new Car("Car");
myCar.Drive();

var mySuperCar = new SuperCar("SuperCar");
mySuperCar.Drive();
mySuperCar.Fly();

Why did the author add the Drive and Fly methods using prototype, and did not declare them as a this.Drive method inside the Car class and as this.Fly in the SuperCar class?

Why does SuperCar.prototype.constructor need to be set back to SuperCar? Is the constructor property overridden when prototype is set? I commented out this line and nothing changed.

Why call Car.call(this, name); in the SuperCar constructor? Won't properties and methods of Car be 'inherited' when I do

var myCar = new Car("Car");

Answer

Tung Nguyen picture Tung Nguyen · May 21, 2009

To add to Norbert Hartl's answer, SuperCar.prototype.constructor isn't needed, but some people use it as a convenient way of getting the constructing function of an object (SuperCar objects in this case).

Just from the first example, Car.call(this, name) is in the SuperCar constructor function because when you do this:

var mySuperCar = new SuperCar("SuperCar");

This is what JavaScript does:

  1. A fresh, blank object is instantiated.
  2. The fresh object's internal prototype is set to Car.
  3. The SuperCar constructor function runs.
  4. The finished object is returned and set in mySuperCar.

Notice how JavaScript didn't call Car for you. Prototypes being as they are, any property or method that you don't set yourself for SuperCar will be looked up in Car. Sometimes this is good, e.g. SuperCar doesn't have a Drive method, but it can share Car's one, so all SuperCars will use the same Drive method. Other times you don't want sharing, like each SuperCar having it's own Name. So how does one go about setting each SuperCar's name to it's own thing? You could set this.Name inside the SuperCar constructor function:

function SuperCar(name){
    this.Name = name;
}

This works, but wait a second. Didn't we do exactly the same thing in the Car constructor? Don't want to repeat ourselves. Since Car sets the name already, let's just call it.

function SuperCar(name){
    this = Car(name);
}

Whoops, you never want to change the special this object reference. Remember the 4 steps? Hang onto that object that JavaScript gave you, because it's the only way to keep the precious internal prototype link between your SuperCar object and Car. So how do we set Name, without repeating ourselves and without throwing away our fresh SuperCar object JavaScript spent so much special effort to prepare for us?

Two things. One: the meaning of this is flexible. Two: Car is a function. It's possible to call Car, not with a pristine, fresh instantiated object, but instead with, say, a SuperCar object. That gives us the final solution, which is part of the first example in your question:

function SuperCar(name){
    Car.call(this, name);
}

As a function, Car is allowed to be invoked with the function's call method, which changes the meaning of this within Car to the SuperCar instance we're building up. Presto! Now each SuperCar gets it's own Name property.

To wrap up, Car.call(this, name) in the SuperCar constructor gives each new SuperCar object it's own unique Name property, but without duplicating the code that's already in Car.

Prototypes aren't scary once you understand them, but they're not much like the classic class/inheritence OOP model at all. I wrote an article about the prototypes concept in JavaScript. It's written for a game engine that uses JavaScript, but it's the same JavaScript engine used by Firefox, so it should all be relevant. Hope this helps.