It turns out it is quite difficult to arrive at a satisfactory definition for inheritance in computer programming.
Some texts describe inheritance in terms of classes — but these are clearly not axiomatic because JavaScript supports inheritance, and yet it does not have classes. Even ES6 classes are not “classical”.
JavaScript is both dynamically and weakly typed. This means that there are no compile-time or runtime checks on types. I can write a function and I am unable to constrain the type of the arguments supplied to it using the syntax of the language.
In addition, at runtime, I can invoke any method with an instance of whatever type I please.
Consider the following:
function foo(p) {
console.log(p);
}foo(1); // Fine...
foo('1'); // ...also fine
foo(function bar(){}); // ...also perfectly fine.
And yet as every developer already “knows”, JavaScript has the following types:
number, string, boolean, object, null and undefined
But number, string and boolean can be said to be optimizations for objects. Indeed, each of these types are boxed automatically into objects for certain operations. In some sense therefore, we can say number, string and boolean are really just convenient shorthands for object instances with specific semantics (immutability, for example).
If this is true then null can be said to be a shorthand for an object with identity indicating an “empty object”. Indeed typeof null returns “object”.
That leaves undefined, which is not really a type at all. Instead it is a keyword used to indicate the absence of anything.
JavaScript also has — in constructor functions — user-defined types. OK, these do not meet the criteria of the static types you can create in class-based languages, but they can be considered abstract-data types (ADTs) of a sort.
And what is an ADT if not a collection of data and functionality with a name? In strict terms, however, the only type implemented by constructor functions and their instances is of course object.
So, when talking about inheritance in JavaScript we must talk about objects, because there is literally nothing else in the language.
A definition
If there was one thing common to all definitions of inheritance, it would probably be the identification of a hierarchy between types (or objects in the case of JavaScript) that permits some kind of delegation between members of said hierarchy.
Given this, how about:
Inheritance in JavaScript is the ability to have an object delegate some or all of its implementation to another, by way of a hierarchical link.
JavaScript can be said to provide such a link natively via the prototype-chain (i.e. the chain of [[Prototype]] references).
Inheritance over time
Prior to ES5, inheritance was achieved thus:
function Parent() {
this.foo = function() {
console.log('foo');
};
}
Parent.prototype.bar = function() {
console.log('bar');
}function Child() {
}Child.prototype = p = new Parent();
Child.prototype.constructor = Child;var c = new Child();
c instanceof Parent; // true
c instanceof Child; // true
c.__proto__ === p; // true
c.__proto__.__proto__ === Parent.prototype; // true
c.__proto__.__proto__.__proto__ === Object.prototype; // true
c.__proto__.__proto__.__proto__.__proto__ === null; // true
c.foo(); // foo
c.bar(); // bar
This configured the inheritance relationship thus:
c => “actual instance of Parent” => Parent.prototype (instance of Object) => Object.prototype (instance of Object) => null
Which is usually exactly what we wanted. The important things work: instanceof behaves as expected, functions on the prototype of the base object Parent are available to us, and Parent.prototype is not the direct prototype of Child (thereby avoiding the possibility of accidental interference with other objects sharing that prototype object).
But this approach has a significant downside in that it requires the actual instantiation of the base constructor function. This is usually not what we want to do. In any event it couples Child and Parent in that Child needs to unnecessarily know how to instantiate a Parent instance — and have the information to do so before the child constructor function is instantiated.
What we really want to do is something like:
Child.prototype = anObjectWithParentPrototypeOnThePrototypeChain;
JavaScript has not historically exposed the references on the prototype chain directly to programmers. So we couldn’t do something like:
Child.prototype = (function () {
var o = {};
o.__proto__ = Parent.prototype;
return o;
}());
Even today, this would be frowned up as it defeats certain optimizations made by JavaScript compilers (V8, for example, will assign internal object “classes” for optimization purposes, based upon how an object was constructed).
Although it must be admitted that the __proto__ has latterly become a recognized de facto standard, it turns out there is (and was in ES3) a way around this issue without resorting to its use.
By making use of an intermediate object, we can construct the prototype chain we need without having to run the base constructor function.
Like so:
// Parent defined as before.function Child() {
Parent.call(this); // Not always required.
}var TempCtor, tempO;
TempCtor = function() {};
TempCtor.prototype = Parent.prototype;
Child.prototype = tempO = new TempCtor();
Child.prototype.constructor = Child;var c = new Child();
c instanceof Parent; // true - Parent.prototype is on the p.-chain
c instanceof Child; // true
c.__proto__ === tempO; // true
// ...and so on, as before
But, not to put too fine a point on it, that’s just tedious.
Douglas Crockford (among many others!) agreed with this sentiment and had Object.create added to the language specifically so that the “inheritance wheel” didn’t have to be re-invented by every JavaScript developer, ever.
So now (post ES5) we can write the following:
// Parent defined as before.function Child() {
Parent.call(this); // Not always required.
}Child.prototype = o = Object.create(Parent.prototype);
Child.prototype.constructor = Child;var c = new Child();
c instanceof Parent; // true - Parent.prototype is on the p.-chain
c instanceof Child; // true
c.__proto__ === o; // true
// ...and so on, as before
Alternative approaches
Douglas Crockford has described an alternative approach to inheritance in JavaScript. He calls this “functional inheritance”.
What he means by this is that he is achieving what are (to him) the important functional advantages of inheritance, without needing to leverage the prototype-chain. Instead, he presents a straightforward mechanism for the decoration of objects with methods.
Although other authors present this approach as “inheritance”, I do not believe it can be correctly described that way because of the missing hierarchical link, as described earlier in this piece.
I believe Crockford presents this technique full-knowing that it does not match the established definition of inheritance. He does this to make a point. He is establishing his position that inheritance is not a useful concept beyond the trivial augmentation of objects (with which I have some sympathy).
For this reason I will not fully describe this approach here. Further information on functional inheritance can be found here.
Summary
Inheritance in JavaScript is achieved via the prototype-chain. This is defined as the chain of [[Prototype]] references starting with any given object.
We have seen how early techniques for configuring the prototype chain had limitations surrounding coupling between child and parent constructor functions. With Object.create, inheritance hierarchies can now be configured in as little as a single line. This technique is sometimes called the pseudo-classical style of inheritance.
My current position is that leveraging the prototype chain in this way is the only mechanism for inheritance in JavaScript per the established definition; although, as Crockford points out, there are other techniques available for JavaScript developers to achieve some of the same effects without doing so.
My name is Ben Aston and thank you for reading my post today. I am a London-based JavaScript consultant, available for short-term consultancy globally. I can be contacted at ben@bj.ma.
If you would like to further develop your knowledge of JavaScript you might be interested in my hands-on training course. Full details can be found online at www.learnjavascript.london.
If you enjoyed this piece, please let me know by clicking the “recommend” button below.
You may also be interested in my post on closures in JavaScript.