shopping24 tech blog

s is for shopping

May 12, 2015 / by Kim Hogeling / Web developer / @kimhogeling

JavaScript inheritance explained very simple

JavaScript inheritance is something explained by a million people in a million different ways. It can be explained so simple, because correct inheritance only takes two simple steps to achieve. Prototype chaining and constructor stealing is all you need. This is my very simple explanation.

TL;DR. Take me to the example!

Prototype chaining

JavaScript inheritance is all about prototype chaining. To create one, simply set the prototype of an instance of a subtype equal to the one of an instance of the supertype. I use Object.create() for that. There is one problem though: how to inherite the supertype’s properties?. This is where constructor stealing comes in.

Constructor stealing

The subtype can inherit supertypes properties, by calling the constructor of the supertype. I use call() for that. Of course apply() can be used instead, if you prefer it. The combination of prototype chaining and constructor stealing is called pseudo classical inheritance, because it is not exactly like in class based languages, but does behave the same. The last problem that remains is how to access the supertype’s methods?

Accessing supertype methods

To access the supertype’s methods, simply call them directly on the prototype. Once again I use call() for that. Again apply() can be used instead.

Example

Enough talk already, lets see it in action!

The supertype class Vehicle

/**
 * Creates a new vehicle.
 * @constructor
 * @classdesc This will be the supertype object.
 * @param {String} color The color for the shiny new vehicle.
 * @returns {Vehicle}
 */
function Vehicle(color) {
    this.color = color;
    // Vehicles will have at least 1 wheel.
    this.amountWheels = 1;
}

/**
 * Describes the vehicle.
 * Method for everything, that is an instance of Vehicle.
 * @public
 * @returns {String}
 */
Vehicle.prototype.toString = function () {
    var text = 'I am a ' + this.color + ' vehicle and have ' + this.amountWheels + ' wheel';
    // Some subtypes might have more than 1 wheel.
    if (this.amountWheels > 1) {
        text += 's';
    }
    return text;
};

/**
 * Honk honk!
 * This method will also be available in every subtype.
 * @public
 * @returns {String}
 */
Vehicle.prototype.honk = function () {
    return 'HONK HONK!';
};

The subtype class Car

/**
 * Creates a new Car.
 * @constructor
 * @classdesc This will be the subtype object.
 * @extends Vehicle
 * @param {string} color The color for the cool new car.
 * @returns {Car}
 */
function Car(color) {
    // Constructor stealing to inherit the color and amountWheels properties.
    Vehicle.call(this, color);
    // Change the amount wheels from 1 to 4.
    this.amountWheels = 4;
    // Car specific property.
    this.distance = 0;
}

/**
 * This prototype chaining makes Vehicle a supertype of Car.
 * This is where the inheritance happens.
 */
Car.prototype = Object.create(Vehicle.prototype);

/**
 * Describes the Car.
 * Subtype's toString method will be a little more enhanced.
 * @public
 * @returns {string}
 */
Car.prototype.toString = function () {
    // Accessing the supertype toString method to reuse it.
    var text = Vehicle.prototype.toString.call(this);
    // The enhanced part.
    text += ' and have driven ' + this.distance + 'km';
    // Correct the text from supertype.
    return text.replace('vehicle', 'car');
};

/**
 * Drive some km.
 * This is a car specific method.
 * @public
 * @param {type} km Distance in km.
 * @returns {undefined}
 */
Car.prototype.drive = function (km) {
    this.distance += km;
};

Showing of the inheritance

// I chose a monocycle as a supertype (Vehicle is not abstract),
// just to compare the available methods of the supertype and subtype.
var mclean = new Vehicle('black');
mclean instanceof Vehicle; // => true
mclean.honk(); // => "HONK HONK!"
mclean.toString(); // => "I am a black vehicle and have 1 wheel"


// I like vw cars, so that will be my subtype.
var vw = new Car('green');

// vw is an instance of Car as well as of Vehicle.
vw instanceof Car; // => true
vw instanceof Vehicle; // => true

// Only Car has the drive method. It does not exist in Vehicle.
vw.drive(42);

// Car does not have it's own honk method. It is inherited from Vehicle.
vw.honk(); // => "HONK HONK!"

// Car's toString method is overwritten from Vehicle and slighlty enhanced.
vw.toString(); // => "I am a green car and have 4 wheels and have driven 42km"