Understanding prototypal inheritance in JavaScript

JavascriptOopInheritanceConstructorPrototype Programming

Javascript Problem Overview


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:

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

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

SuperCar.prototype = new Car();
SuperCar.prototype.constructor = SuperCar;

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

SuperCar.prototype.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();

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");

Javascript Solutions


Solution 1 - Javascript

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.

Solution 2 - Javascript

The two blocks differ in a way that in the first example Drive() will only exist once while at the second approach Drive() will exist per instance (Every time you do new Car() the function drive() will be created again). Or different said the first uses the prototype to store the function and the second the constructor. The lookup for functions is constructor and then prototype. So for your lookup of Drive() it finds it regardless if it is in the constructor or in the prototype. Using the prototype is more efficient because usually you need a function only once per type.

The new call in javascript automatically sets the constructor in the prototype. If you are overwriting the prototype so you have to set the constructor manually.

Inheritance in javascript has nothing like super. So if you have a subclass the only chance to call the super constructor is by its name.

Solution 3 - Javascript

Norbert, you should note that your first example is pretty much what Douglas Crockford calls pseudoclassical inheritance. Something things to note about this:

  1. You will call the Car constructor twice, once from the SuperCar.prototype = new Car() line and the other from the "constructor stealing" line Car.call(this...you can create a helper method to inherit prototypes instead and your Car constructor will only have to run once making the setup more efficient.
  2. The SuperCar.prototype.constructor = SuperCar line will allow you to use instanceof to identify the constructor. Some folks want this others just avoid using instanceof
  3. Reference vars like: var arr = ['one','two'] when defined on the super (eg Car) will get shared by ALL instances. This means inst1.arr.push['three'], inst2.arr.push['four'], etc., will show up for all instances! Essentially, static behavior that you probably don't want.
  4. You second block defines the fly method in the constructor. This means for every time that it's called, a "method object" will be created. Better to use a prototype for methods! You CAN however keep it in the constructor if you'd like - you just need to guard so you only actually initialize the prototype literal once (pseudo): if (SuperCar.prototype.myMethod != 'function')...then define your prototype literal.
  5. 'Why call Car.call(this, name)....': I don't have time to look carefully at your code so I may be wrong but this is usually so that each instance can keep it's own state to fix the 'staticy' behavior issue of prototype chaining that I described above.

Lastly, I'd like to mention that I have several examples of TDD JavaScript Inheritance code that works here: TDD JavaScript Inheritance Code and Essay I'd love to get your feedback as I'm hoping to improve it and keep it open source. The goal is to help classical programmers get up to speed with JavaScript quickly and also supplement the study both Crockford and Zakas books.

Solution 4 - Javascript

I am not 100% sure, but I believe the difference is that the second example simply duplicates the contents of the Car class into the SuperCar object, while the first links the SuperCar prototype to the Car class, so that run-time changes to the Car class affect the SuperCar class as well.

Solution 5 - Javascript

function abc() {
}

Prototype methods and property created for function abc

abc.prototype.testProperty = 'Hi, I am prototype property';
abc.prototype.testMethod = function() { 
   alert('Hi i am prototype method')
}

Creating new instances for function abc

var objx = new abc();

console.log(objx.testProperty); // will display Hi, I am prototype property
objx.testMethod();// alert Hi i am prototype method

var objy = new abc();

console.log(objy.testProperty); //will display Hi, I am prototype property
objy.testProperty = Hi, I am over-ridden prototype property

console.log(objy.testProperty); //will display Hi, I am over-ridden prototype property

http://astutejs.blogspot.in/2015/10/javascript-prototype-is-easy.html

Solution 6 - Javascript

There are several questions here:

> Can you please explain the difference between the following blocks of code. I tested and both blocks work.

The first only creates one Drive function, the second creates two of them: one on myCar and another one on mySuperCar.

Here is code that would give different results when either the first or second block was executed:

myCar.Fly === mySuperCar.Fly // true only in the first case
Object.keys(myCar).includes("Fly") // true only in the second case
Object.keys(Car.prototype).length === 0 // true only in the second case

> What's the best practice and why?
> Why did the author add Drive and Fly methods using prototype, but doesn't declare them as this.Drive method inside Car class and this.Fly in SuperCar class?

It is better practice to define methods on the prototype, because:

  • each method is only defined once
  • each method is also available to instances that were created without executing the constructor (which is the case when calling Object.create(Car.prototype));
  • you can inspect at which level in an instance's prototype chain a certain method got defined.

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

The constructor property is not overridden when prototype is set. But the constructor of new Car() is Car, so if you set new Car() to SuperCar.prototype, then obviously SuperCar.prototype.constructor is Car.

As long as you don't reassign to prototype, there is an invariance: Constructor.prototype.constructor === Constructor. For example, this is true for Car: Car.prototype.constructor === Car, but it is equally true for Array, Object, String, ...etc.

But if you reassign a different object to prototype, that invariance is broken. Usually this is not a problem (as you have noticed), but it is better to reinstate it, because it answers the question "Which constructor uses this prototype object when creating new instances?" Some code may do such inspection and depend on it. See "Why is it necessary to set the prototype constructor?" for such cases.

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

> var myCar = new Car("Car");

If you don't do Car.call(this, name); then your SuperCar instance will not have a name property. You could of course decide to just do this.name = name; instead, which just copies the code that is in the Car constructor, but in more complex situations it would be bad practice to have such code duplication.

It would not be helpful to call new Car(name) within the SuperCar constructor, as that will create another object, while you really need to extend the this object. By not using new (using call instead) you actually tell the Car function to not run as a constructor (i.e. to not create a new object), but to use the object you pass to it instead.

###Times have changed

In modern versions of JavaScript you can use super(name) instead of Car.call(this, name):

function SuperCar(name) {
    super(name);
}

Today, you would also use the class syntax and write the first code block from the question as follows:

class Car {
    constructor(name) {
        this.name = name;
    }
    drive() {
        console.log(`My name is ${this.name} and I'm driving.`);
    }
}

class SuperCar extends Car {
    constructor(name) {
        super(name);
    }
    fly() {
        console.log(`My name is ${this.name} and I'm flying!`);
    }
}

const myCar = new Car("Car");
myCar.drive();

const mySuperCar = new SuperCar("SuperCar");
mySuperCar.drive();
mySuperCar.fly();

Note how you don't even have to mention the prototype property to achieve the goal. The class ... extends syntax also takes care of setting the prototype.constructor property as the first block in your question did.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDasha SaloView Question on Stackoverflow
Solution 1 - JavascriptTung NguyenView Answer on Stackoverflow
Solution 2 - JavascriptNorbert HartlView Answer on Stackoverflow
Solution 3 - JavascriptRobView Answer on Stackoverflow
Solution 4 - JavascriptJeff OberView Answer on Stackoverflow
Solution 5 - JavascriptJack SaneView Answer on Stackoverflow
Solution 6 - JavascripttrincotView Answer on Stackoverflow