Since I last wrote about inheritance in JavaScript (JS), ECMAScript 2015, a.k.a. ES6, introduced class syntax in an effort to bridge the gap between JS’s Prototypal Inheritance and the traditional Classical Inheritance of Object-Oriented (OO) programming languages like Java and C++. Having said that, this article is not about the new syntax. With somewhat lackluster browser support, it might be a tad premature to jump on that bandwagon. Moreover, having written JavaScript code for about 20 years now, I have come to appreciate JavaScript, in all its wonderful quirkiness, including the wild and wonderful Prototypal Inheritance. So, in today’s article, I’d like to take another look at Prototypal Inheritance, and explore its specific advantages and disadvantages, with the outlook that it isn’t necessarily any better or worse than the class inheritance of OO programming.
Prototypal vs. Classical Inheritance
Seeing that the makers of ECMAScript are overtly mimicking the class syntax of OO languages, one has to ask, does that imply that they recognize an inherent weakness in Prototypal Inheritance? Not necessarily. In my opinion, the decision is probably more in response to the ongoing confusion experienced by many JS coders who were well versed in classical inheritance mechanisms. Their attempts to make JavaScript objects behave like OO classes proved to be frustrating and problematic exercise.
Of course, the problem with that, is that the path to gaining the most from any language is to make the most of its individual idiosyncrasies. Conversely, trying to emulate the syntax of another language tends to render it less potent.
In classical OO programming, developers create objects which are abstractions of real world entities. There are two types of abstractions: classes and objects. An object is an abstraction of a real world entity, whereas a class is a generalization of an object. For example, a Vehicle is a generalization of a Car. Hence, cars (class) inherit from vehicles (object).
In prototypal inheritance, objects are abstractions of either real world entities (in which case they are simply called objects) or other objects (in which case they are called prototypes of those objects which they abstract). Hence, a prototype is a generalization.
The difference between classical inheritance and prototypal inheritance is that classical inheritance is limited to classes inheriting from other classes while prototypal inheritance supports the cloning of any object using an object linking mechanism. Without getting into too much of the nitty-gritty details, a prototype basically acts as a template for other objects, whether they are extending the base object or not.
A Simple Inheritance Example
The following JS code creates a parent constructor for a Vehicle and a method named start(). The Car object then inherits the start() method by passing the Vehicle’s prototype to the Object.create() method. Its start() method overrides the parent’s, while calling it from within the function body:
// Vehicle - parent function Vehicle(name) { this.name = name; } // parent method Vehicle.prototype.start = function() { return "Engine of " + this.name + " starting…"; }; // Car - child function Car(name) { Vehicle.call(this, name); // call super constructor. } // child extends parent Car.prototype = Object.create(Vehicle.prototype); // child method Car.prototype.start = function() { console.log("Gidday! " + Vehicle.prototype.start.call(this)); }; // instances of child var c1 = new Car("Infiniti G37"); // accessing the child method which internally access parent method c1.start(); // outputs: "Gidday! Engine of Infiniti G37 starting…"
Dangers of Prototypal Inheritance
One of Prototypal Inheritance’s greatest strengths – and inherent dangers – is its ability to overridden at runtime, thus changing the behavior of all objects that are based on that particular prototype.
Consider the following example that updates the Number’s toString() method by prefixing a dollar sign to its output:
var price = 10.99, justANumber = 42, original_toString = Number.prototype.toString; document.writeln('price before toString() update: '+price.toString()+'<br/>'); Number.prototype.toString = function(radix) { return '$'+original_toString.call(this, radix); }; document.writeln('price after toString() update: '+price.toString()+'<br/>'); document.writeln('Oh oh. justANumber: '+justANumber.toString()+'??!<br/>'); /* outputs: price before toString() update: 10.99 price after toString() update: $10.99 Oh oh. justANumber: $42??! */
Now all numbers include the dollar sign, whether we want it or not!
This code is also up on Codepen.io.
A More Complete Example
Here’s another example of Prototypal Inheritance that demonstrates multiple objects inheriting from the same template:
// Vehicle - parent function Vehicle(name) { this.name = name; } // parent method Vehicle.prototype.start = function() { return "Engine of " + this.name + " starting…"; }; // Car - child function Car(name) { Vehicle.call(this, name); // call super constructor. } // child extends parent Car.prototype = Object.create(Vehicle.prototype); // child method Car.prototype.start = function() { console.log("Gidday! " + Vehicle.prototype.start.call(this)); }; // Van - child function Van(name) { Vehicle.call(this, name); // call super constructor. } // child extends parent Van.prototype = Object.create(Vehicle.prototype); // instances of children var c1 = new Car("Infiniti G37"); var c2 = new Car("Ford Mustang"); var v1 = new Van("Dodge Caravan"); // accessing the child method which internally access parent method c1.start(); c2.start(); //using parent start() console.log(v1.start()); // add our own start() method Van.prototype.start = function() { console.log("Hello! " + Vehicle.prototype.start.call(this)); }; v1.start(); /* outputs: Gidday! Engine of Infiniti G37 starting… Gidday! Engine of Ford Mustang starting… Engine of Dodge Caravan starting… Hello! Engine of Dodge Caravan starting… */
You can view the above code on Codepen.io as well.
Conclusion
In this article we explored some of the differences between Classical and Prototypal Inheritance, considered some of the dangers of the latter, and saw how to implement inheritance using prototypes. Whether or not you agree with the inclusion of classes to the JS language, Prototypal Inheritance stands on its own as an elegant inheritance mechanism. As such, it is perfectly well-suited to link objects together so that they share common properties and functionality for a variety of applications.