Object-orientation, prototyping, and inheritance

So far, we haven't talked about inheritance in JavaScript, so let's do this now.

It's useful to share behaviour within a certain class of objects, but there are cases where we would like to share behaviour between different, but similar classes of objects.

Imagine our virtual world not only had cars, but also bikes. Both drive, but where a car has a horn, a bike has a bell.

Being able to drive makes both objects vehicles, but not sharing the honk and ring behaviour distinguishes them.

We could illustrate their shared and local behaviour as well as their relationship to each other as follows:


Designing this relationship in a class-based language like Java is straightforward: We would define a class Vehicle with a method drive, and two classes Car and Bike which both extend the Vehicle class, and implement a honk and a ring method, respectively.

This would make the car as well as bike objects inherit the drive behaviour through the inheritance of their classes.

How does this work in JavaScript, where we don't have classes, but prototypes?

Let's look at an example first, and then dissect it. To keep the code short for now, let's only start with a car that inherits from a vehicle:

var Vehicle = function() {};

Vehicle.prototype.drive = function() {
  console.log('vrooom...');
};


var Car = function() {};

Car.prototype = new Vehicle();

Car.prototype.honk = function() {
  console.log('honk honk');
};


var myCar = new Car();

myCar.honk();   // outputs "honk honk"
myCar.drive();  // outputs "vrooom..."

In JavaScript, inheritance runs through a chain of prototypes.

The prototype of the Car constructor is set to a newly created vehicle object, which establishes the link structure that allows the interpreter to look for methods in parent objects.

The prototype of the Vehicle constructor has a function drive. Here is what happens when the myCar object is asked to drive():

  • The interpreter looks for a drive method within the myCar object, which does not exist
  • The interpreter then asks the myCar object for its prototype, which is the prototype of its constructor Car
  • When looking at Car.prototype, the interpreter sees a vehicle object which has a function honk attached, but no drive function
  • Thus, the interpreter now asks this vehicle object for its prototype, which is the prototype of its constructor Vehicle
  • When looking at Vehicle.prototype, the interpreter sees an object which has a drive function attached  the interpreter now knows which code implements the myCar.drive() behaviour, and executes it