Aadit M Shah

Gravatar

Why Prototypal Inheritance Matters

Published: 26 May 2013 | Comments

Five days ago I wrote Standardizing Harmony Classes. It showed how to simulate classes in current JavaScript implementations and how ECMAScript Harmony classes are just syntactic sugar for the same. My programming style has evolved since then, thanks to Om Shankar and the awesome members of the JavaScript Room; and like Douglas Crockford did back in 2006, I too have learned to fully embrace prototypalism.

You see JavaScript is a multi-paradigm programming language. It has both object-oriented and functional features, which means that you can write programs using both these styles. However these two paradigms don't mix well. For example it's not possible to use new (a classical object-oriented feature) with apply (a functional feature). Prototypal inheritance is used to bridge this gap.

Contents

  1. The Problem with Classical Inheritance
  2. Stop Using the new Keyword
  3. Understanding Prototypal Inheritance
    1. Ex Nihilo Object Creation
    2. Cloning an Existing Object
    3. Extending a Newly Created Object
    4. Constructors vs Prototypes
    5. Combining Object Creation and Extension
    6. Two Methods of Prototypal Inheritance
      1. Delegation or Differential Inheritance
      2. Cloning or Concatenative Inheritance
    7. Inheriting from Multiple Prototypes
    8. Blueprints for Mixins
    9. Fixing the instanceof Operator
    10. Propagating Changes to Prototypes
  4. Conclusion
  5. Related Articles
  6. Comments

The Problem with Classical Inheritance

Most JavaScript programmers will tell you that classical inheritance is bad. However only a handful of them really know why. The truth is that classical inheritance is not bad. Python has classical inheritance and it's a great programming language. Nevertheless classical inheritance is not suitable for JavaScript. Python got classes right. They are simply factory functions. In JavaScript however any function can be used as a constructor.

The problem with JavaScript is that since any function can be used as a constructor we need to distinguish a normal function call from a constructor function call; and this is achieved using the new keyword. However, this breaks functional features in JavaScript since new is a keyword, not a function. Hence functional features can't be used in conjunction with object instantiation.

1 function Person(firstname, lastname) {
2     this.firstname = firstname;
3     this.lastname = lastname;
4 }

Consider the above program. You can create an instance of the function Person by calling it, with the function call preceded by the new keyword:

var author = new Person("Aadit", "Shah");

However there's no way to apply an array of arguments to the constructor function:

var author = new Person.apply(null, ["Aadit", "Shah"]); // error

Nevertheless, if new was a function then it would be possible:

var author = Person.new.apply(Person, ["Aadit", "Shah"]);

Fortunately since JavaScript has prototypal inheritance it's possible to implement new as a function:

1 Function.prototype.new = function () {
2     function functor() { return constructor.apply(this, args); }
3     var args = Array.prototype.slice.call(arguments);
4     functor.prototype = this.prototype;
5     var constructor = this;
6     return new functor;
7 };

This is not possible in languages like Java in which objects can only be created by instantiating classes using the new keyword.

The following table lists the advantages of prototypal inheritance over classical inheritance:

Classical Inheritance Prototypal Inheritance
Classes are immutable. You can't modify or add new methods to them at runtime. Prototypes are flexible. They may be either mutable or immutable.
Classes may or may not support multiple inheritance. Objects can inherit from multiple prototypes.
It's verbose and complicated. You have abstract classes, final classes, interfaces, etc. It's simple. You only have objects and extending objects is the only operation required.

Stop Using the new Keyword

By now you must know why I believe that the new keyword is bad - you can't use it in conjunction with functional features. However that's no reason to stop using it. The new keyword has legitimate uses. Nevertheless I still advise you to stop using it. The new keyword masks true prototypal inheritance in JavaScript, making it look more like classical inheritance. As Raynos states:

new is a remnant of the days where JavaScript accepted a Java like syntax for gaining "popularity".

JavaScript is a prototypal language with its roots in Self. However, for marketing purposes Brendan Eich was keen to push it as Java's little brother:

And we were pushing it as a little brother to Java, as a complementary language like Visual Basic was to C++ in Microsoft’s language families at the time.

This design decision however caused new problems (see what I did there?). When people see the new keyword in JavaScript they think about classes, and then when it comes inheritance they get stumped. As Douglas Crockford stated:

This indirection was intended to make the language seem more familiar to classically trained programmers, but failed to do that, as we can see from the very low opinion Java programmers have of JavaScript. JavaScript's constructor pattern did not appeal to the classical crowd. It also obscured JavaScript's true prototypal nature. As a result, there are very few programmers who know how to use the language effectively.

Hence I advise you to stop using the new keyword. JavaScript has a much more powerful prototypal system underneath its classical object-oriented cruft. However most programmers never see this and hence remain in the dark.

Understanding Prototypal Inheritance

Prototypal inheritance is simple. In prototypal languages you only have objects. No classes. There are two ways to create new objects - ex nihilo ("out of nothing") object creation or through cloning an existing object. In JavaScript the Object.create function (discovered by Douglas Crockford) is used to create new objects. Newly created objects are then extended with new properties.

Ex Nihilo Object Creation

The Object.create function in JavaScript is used to create objects ex nihilo as follows:

var object = Object.create(null);

In the above example the newly created object has no prototype. It's a clone of null.

Cloning an Existing Object

The Object.create function can also be used to clone an existing object as follows:

1 var rectangle = {
2     area: function () {
3         return this.width * this.height;
4     }
5 };
6 
7 var rect = Object.create(rectangle);

In the above example rect inherits the area function from rectangle. Also notice that rectangle is an object literal. An object literal is a succinct way of creating a clone of Object.prototype and extending it with new properties. It's equivalent to:

1 var rectangle = Object.create(Object.prototype);
2 
3 rectangle.area = function () {
4     return this.width * this.height;
5 };

Extending a Newly Created Object

In the above example we cloned the rectangle object and called it rect, but before we may use the area function of rect we need to extend it with width and height properties as follows:

1 rect.width = 5;
2 rect.height = 10;
3 alert(rect.area());

However this is a very clumsy way to create a clone of an object and extend it with new properties. We need to manually define width and height on every clone of rectangle. It would be nice to have a function create a clone of rectangle and extend it with width and height properties for us. Sounds familiar? It is. I'm talking about a constructor function. Let's call this function create and define it on the rectangle object itself:

 1 var rectangle = {
 2     create: function (width, height) {
 3         var self = Object.create(this);
 4         self.height = height;
 5         self.width = width;
 6         return self;
 7     },
 8     area: function () {
 9         return this.width * this.height;
10     }
11 };
12 
13 var rect = rectangle.create(5, 10);
14 
15 alert(rect.area());

Constructors vs Prototypes

Wait a moment. This looks a lot like the normal constructor pattern in JavaScript:

 1 function Rectangle(width, height) {
 2     this.height = height;
 3     this.width = width;
 4 }
 5 
 6 Rectangle.prototype.area = function () {
 7     return this.width * this.height;
 8 };
 9 
10 var rect = new Rectangle(5, 10);
11 
12 alert(rect.area());

Yes, indeed it is. In order to make JavaScript look more like Java the prototypal pattern was inverted to yield the constructor pattern. Hence every function in JavaScript has a prototype object and can be used as a constructor. The new keyword allows us to use a function as a constructor. In addition it clones the prototype of the constructor and binds it to the this pointer of the constructor, returning this if no other object is returned.

Both the prototypal pattern and the constructor pattern are equivalent. Hence you may wonder why anybody would bother using the prototypal pattern over the constructor pattern. After all the constructor pattern is more succinct than the prototypal pattern. Nevertheless the prototypal pattern has many advantages over the constructor pattern, enlisted in the following table:

Constructor Pattern Prototypal Pattern
Functional features can't be used in conjunction with the new keyword. Functional features can be used in conjunction with create.
Forgetting to use new leads to unexpected bugs and global variables. Since create is a function the program will always work as expected.
Prototypal inheritance is unnecessarily complicated and confusing. Prototypal inheritance is simple and easy to understand.

The last point may need some explanation. The underlying idea is that prototypal inheritance using constructors is more complicated than prototypal inheritance using prototypes. Let's consider prototypal inheritance using prototypes first:

1 var square = Object.create(rectangle);
2 
3 square.create = function (side) {
4     return rectangle.create.call(this, side, side);
5 };
6 
7 var sq = square.create(5);
8 
9 alert(sq.area());

It's easy to understand what's happening here. First we create a clone of rectangle and call it square. Next we override the create function of square with a new create function. Finally we call the create function of rectangle from the new create function and return the object is returns. In constrast prototypal inheritance using constructors looks like this:

 1 function Square() {
 2     Rectangle.call(this, side, side);
 3 }
 4 
 5 Square.prototype = Object.create(Rectangle.prototype);
 6 
 7 Square.prototype.constructor = Square;
 8 
 9 var sq = new Square(5);
10 
11 alert(sq.area());

Sure, the constructor function becomes simpler. However it becomes very difficult to explain prototypal inheritance to a person who knows nothing about it. It becomes even more difficult to explain it to a person who knows classical inheritance.

When using the prototypal pattern it becomes obvious that one object inherits from another object. When using the constructor pattern this is not so obvious because you tend to think in terms of constructors inheriting from other constructors.

Combining Object Creation and Extension

In the previous example we created a clone of rectangle and called it square. Then we extended it with a new create property, overriding the create function inherited from rectangle. It would be nice to combine these two operations into one, just like object literals are used to create clones of Object.prototype and extend it with new properties. This operation, called extend, can be implemented as a function:

 1 Object.prototype.extend = function (extension) {
 2     var hasOwnProperty = Object.hasOwnProperty;
 3     var object = Object.create(this);
 4 
 5     for (var property in extension)
 6         if (hasOwnProperty.call(extension, property) ||
 7             typeof object[property] === "undefined")
 8                 object[property] = extension[property];
 9 
10     return object;
11 };

Using the above extend function we can rewrite the code for square as follows:

1 var square = rectangle.extend({
2     create: function (side) {
3         return rectangle.create.call(this, side, side);
4     }
5 });
6 
7 var sq = square.create(5);
8 
9 alert(sq.area());

The extend function is the only operation required for prototypal inheritance. It's a superset of the Object.create function and hence it can be used for both object creation and extension, making Object.create obsolete. Hence we can rewrite the code for rectangle using extend as follows, making the create function more structured like the module pattern:

 1 var rectangle = {
 2     create: function (width, height) {
 3         return this.extend({
 4             height: height,
 5             width: width
 6         });
 7     },
 8     area: function () {
 9         return this.width * this.height;
10     }
11 };
12 
13 var rect = rectangle.create(5, 10);
14 
15 alert(rect.area());

Two Methods of Prototypal Inheritance

Some of you may have noticed that the object returned by the extend function actually inherits properties from two objects, and not one - the object being extended and the object extending it. In addition the way in which properties are inherited from these two objects is also different. In the first case we inherit properties via delegation. In the second case we inherit properties via concatenation.

Delegation or Differential Inheritance

Most JavaScript programmers are familiar with differential inheritance. To quote from Wikipedia:

It operates on the principle that most objects are derived from other, more general objects, and only differ in a few small aspects; while usually maintaining a list of pointers internally to other objects which the object differs from.

Prototypal inheritance in JavaScript is based on differential inheritance. Every object in JavaScript has an internal pointer, called [[proto]], which points to the prototype of that object. The [[proto]] property of objects created ex nihilo point to null. This forms a chain of objects linked via the internal [[proto]] property (hence called a prototype chain), which ends in null.

When you try to access a property of an object the JavaScript engine first searches for that property on the object itself. If it cannot find the property on the object then it delegates the property access to the prototype of the object. In this way the property access traverses up the prototype chain until the property is found (in which case it's returned) or the chain ends in null (in which case undefined is returned).

1 function get(object, property) {
2     if (!Object.hasOwnProperty.call(object, property)) {
3         var prototype = Object.getPrototypeOf(object);
4         if (prototype) return get(prototype, property);
5     } else return object[property];
6 }

If the member operators in JavaScript were functions they would be implemented as shown in the program above. It shows how property access is delegated to the prototype of an object if it's not found the object itself.

Cloning or Concatenative Inheritance

Most JavaScript programmers would argue that copying the properties of one object to another is not truly a form of inheritance because any modifications to the original object are not reflected on the clone. Five days ago I would have agreed. Now however I believe that concatenation is a true form of prototypal inheritance. Modifications to the original object can be propagated to its copies to achieve true prototypal inheritance.

Concatenation and delegation both have their advantages and disadvantages. They are listed in the following table:

Delegation Concatenation
Any changes to the prototype are automatically reflected on all its clones. Any changes to the prototype need to be propagated to all its clones.
Property access is slower because it may need to traverse up the prototype chain. Property access is faster because inherited properties are copied.
Objects may only delegate to a single prototype in JavaScript. Objects may copy properties from any number of prototypes.

Inheriting from Multiple Prototypes

The last point in the above table tells us that objects can inherit from multiple prototypes via concatenation. This is an important feature because it proves that prototypal inheritance is more powerful than classical inheritance in Java and as powerful as classical inheritance in C++. To implement multiple inheritance in the prototypal pattern you only need to modify the extend function to copy the properties of multiple prototypes:

 1 Object.prototype.extend = function () {
 2     var hasOwnProperty = Object.hasOwnProperty;
 3     var object = Object.create(this);
 4     var length = arguments.length;
 5     var index = length;
 6 
 7     while (index) {
 8         var extension = arguments[length - (index--)];
 9 
10         for (var property in extension)
11             if (hasOwnProperty.call(extension, property) ||
12                 typeof object[property] === "undefined")
13                     object[property] = extension[property];
14     }
15 
16 
17     return object;
18 };

Multiple inheritance is useful as it promotes modularity and code reusability. Objects inherit from one prototype via delegation and from the rest via concatenation. For example say you have a prototype for an event emitter as follows:

 1 var eventEmitter = {
 2     on: function (event, listener) {
 3         if (typeof this[event] !== "undefined")
 4             this[event].push(listener);
 5         else this[event] = [listener];
 6     },
 7     emit: function (event) {
 8         if (typeof this[event] !== "undefined") {
 9             var listeners = this[event];
10             var length = listeners.length, index = length;
11             var args = Array.prototype.slice.call(arguments, 1);
12 
13             while (index) {
14                 var listener = listeners[length - (index--)];
15                 listener.apply(this, args);
16             }
17         }
18     }
19 };

Now you want square to behave like an event emitter. Since square already inherits from rectangle via delegation it must inherit from eventEmitter via concatenation. This change is very simple to implement using the extend function as follows:

 1 var square = rectangle.extend(eventEmitter, {
 2     create: function (side) {
 3         return rectangle.create.call(this, side, side);
 4     },
 5     resize: function (newSize) {
 6         var oldSize = this.width;
 7         this.width = this.height = newSize;
 8         this.emit("resize", oldSize, newSize);
 9     }
10 });
11 
12 var sq = square.create(5);
13 
14 sq.on("resize", function (oldSize, newSize) {
15     alert("sq resized from " + oldSize + " to " + newSize + ".");
16 });
17 
18 sq.resize(10);
19 
20 alert(sq.area());

It's impossible to implement the above program in Java since it doesn't support multiple inheritance. Instead you would either have to create a separate EventEmitter class or use an EventEmitter interface and implement the on and emit functions separately for each class that implements it. Of course you wouldn't encounter this problem in C++ but as we all know Java sucks.

Blueprints for Mixins

In the previous example you must have noticed that the eventEmitter prototype didn't have a create function. This is because you shouldn't be able to directly create an eventEmitter object. Instead eventEmitter is used as a prototype for other prototypes. Such prototypes are called mixins. They are the prototypal equivalent of abstract classes. Mixins are used to extend the behavior of objects by providing a set of reusable functions.

Sometimes however mixins require private state. For example the eventEmitter mixin would be more secure if it stored its event listeners in a private variable instead of on the this object. However mixins have no create function to encapsulate private state. Hence we create "blueprints" of mixins to create closures. Blueprints may look like constructor functions but they are not meant to be used as constructors. For example:

 1 function eventEmitter() {
 2     var events = Object.create(null);
 3 
 4     this.on = function (event, listener) {
 5         if (typeof events[event] !== "undefined")
 6             events[event].push(listener);
 7         else events[event] = [listener];
 8     };
 9 
10     this.emit = function (event) {
11         if (typeof events[event] !== "undefined") {
12             var listeners = events[event];
13             var length = listeners.length, index = length;
14             var args = Array.prototype.slice.call(arguments, 1);
15 
16             while (index) {
17                 var listener = listeners[length - (index--)];
18                 listener.apply(this, args);
19             }
20         }
21     };
22 }

A blueprint is used to extend an object via concatenation after it's created. Eric Elliot calls them closure prototypes. The code for square would be written as follows if we used the blueprint version of eventEmitter instead:

 1 var square = rectangle.extend({
 2     create: function (side) {
 3         var self = rectangle.create.call(this, side, side);
 4         eventEmitter.call(self);
 5         return self;
 6     },
 7     resize: function (newSize) {
 8         var oldSize = this.width;
 9         this.width = this.height = newSize;
10         this.emit("resize", oldSize, newSize);
11     }
12 });
13 
14 var sq = square.create(5);
15 
16 sq.on("resize", function (oldSize, newSize) {
17     alert("sq resized from " + oldSize + " to " + newSize + ".");
18 });
19 
20 sq.resize(10);
21 
22 alert(sq.area());

Blueprints are unique to JavaScript. They are a powerful feature. However they have their own disadvantages. The following table compares the advantages and disadvantages of mixins and blueprints:

Mixins Blueprints
They are used to extend prototypes of objects. Hence objects share the same blueprint functions. They are used to extend newly created objects. Hence every object has its own set of blueprint functions.
No private state due to lack of an encapsulating function. They are functions, and hence they encapsulate private state.
They are static prototypes and can't be customized. They can be passed arguments to customize the object.

Fixing the instanceof Operator

Many JavaScript programmers would argue that using the prototypal pattern for inheritance is against the spirit of the language. They favor the constructor pattern because they believe that objects created using constructors are actual "instances", since the instanceof operator yields true. However, this argument is moot because the instanceof operator can be implemented in JavaScript as follows:

 1 Object.prototype.instanceof = function (prototype) {
 2     var object = this;
 3 
 4     do {
 5         if (object === prototype) return true;
 6         var object = Object.getPrototypeOf(object);
 7     } while (object);
 8 
 9     return false;
10 };

The instanceof function can now be used to test whether an object inherits from a prototype via delegation. For example:

sq.instanceof(square);

However it's not possible to determine whether an object inherits from a prototype via concatenation as instance relationship information is lost. To solve this problem we save the references of all the clones of a prototype on the prototype itself, and use this information to determine whether an object is an instance of a prototype. This can be done by modifying the extend function as follows:

 1 Object.prototype.extend = function () {
 2     var hasOwnProperty = Object.hasOwnProperty;
 3     var object = Object.create(this);
 4     var length = arguments.length;
 5     var index = length;
 6 
 7     while (index) {
 8         var extension = arguments[length - (index--)];
 9 
10         for (var property in extension)
11             if (property !== "clones" &&
12                 hasOwnProperty.call(extension, property) ||
13                 typeof object[property] === "undefined")
14                     object[property] = extension[property];
15 
16         if (hasOwnProperty.call(extension, "clones"))
17             extension.clones.unshift(object);
18         else extension.clones = [object];
19     }
20 
21     return object;
22 };

Objects that inherit from prototypes via concatenation form a tree of clones which starts from the root object and proceeds down to the leaf objects. A clone chain is a single path from the root object to a leaf object and it's similar to a reverse prototype chain. We use this information to determine whether an object inherits from a prototype via concatenation as follows:

 1 Object.prototype.instanceof = function (prototype) {
 2     if (Object.hasOwnProperty.call(prototype, "clones"))
 3         var clones = prototype.clones;
 4     var object = this;
 5 
 6     do {
 7         if (object === prototype ||
 8             clones && clones.indexOf(object) >= 0)
 9                 return true;
10 
11         var object = Object.getPrototypeOf(object);
12     } while (object);
13 
14     return false;
15 };

The instanceof function can now be used to test whether an object inherits from a prototype via concatenation. For example:

sq.instanceof(eventEmitter);

In the above program the instanceof function will return true if we use the mixin version of eventEmitter. However it will return false if we use the blueprint version of eventEmitter. To solve this problem we create a blueprint function which takes a blueprint as an argument, adds the clones property to it and returns a new blueprint which records its clones:

 1 function blueprint(f) {
 2     var g = function () {
 3         f.apply(this, arguments);
 4         g.clones.unshift(this);
 5     };
 6 
 7     g.clones = [];
 8 
 9     return g;
10 };
11 
12 var eventEmitter = blueprint(function () {
13     var events = Object.create(null);
14 
15     this.on = function (event, listener) {
16         if (typeof events[event] !== "undefined")
17             events[event].push(listener);
18         else events[event] = [listener];
19     };
20 
21     this.emit = function (event) {
22         if (typeof events[event] !== "undefined") {
23             var listeners = events[event];
24             var length = listeners.length, index = length;
25             var args = Array.prototype.slice.call(arguments, 1);
26 
27             while (index) {
28                 var listener = listeners[length - (index--)];
29                 listener.apply(this, args);
30             }
31         }
32     };
33 });

Propagating Changes to Prototypes

The clones property in the previous example serves a dual purpose. It's used to determine whether an object inherits from a prototype via concatenation, and it's used to propagate changes made to a prototype to all its clones. The main advantage of prototypal inheritance over classical inheritance is that you can modify a prototype after it's created. To enable clones to inherit modifications made to a prototype we create a function called define:

 1 Object.prototype.define = function (property, value) {
 2     this[property] = value;
 3 
 4     if (Object.hasOwnProperty.call(this, "clones")) {
 5         var clones = this.clones;
 6         var length = clones.length;
 7 
 8         while (length) {
 9             var clone = clones[--length];
10             if (typeof clone[property] === "undefined")
11                 clone.define(property, value);
12         }
13     }
14 };

Now we can make modifications to a prototype and the changes will be reflected in all the clones. For example we can create an alias addEventListener for the on function of the eventEmitter mixin:

 1 var square = rectangle.extend(eventEmitter, {
 2     create: function (side) {
 3         return rectangle.create.call(this, side, side);
 4     },
 5     resize: function (newSize) {
 6         var oldSize = this.width;
 7         this.width = this.height = newSize;
 8         this.emit("resize", oldSize, newSize);
 9     }
10 });
11 
12 var sq = square.create(5);
13 
14 eventEmitter.define("addEventListener", eventEmitter.on);
15 
16 sq.addEventListener("resize", function (oldSize, newSize) {
17     alert("sq resized from " + oldSize + " to " + newSize + ".");
18 });
19 
20 sq.resize(10);
21 
22 alert(sq.area());

Blueprints need some special attention. Although changes made to blueprints will be propagated to its clones, yet new clones of blueprints will not reflect these changes. Fortunately the solution to this problem is simple. We only need to make a minor modification to the blueprint function as follows, and any changes made to a blueprint created by blueprint will be reflected in all its clones.

 1 function blueprint(f) {
 2     var g = function () {
 3         f.apply(this, arguments);
 4         g.clones.unshift(this);
 5 
 6         var hasOwnProperty = Object.hasOwnProperty;
 7 
 8         for (var property in g)
 9             if (property !== "clones" &&
10                 hasOwnProperty.call(g, property))
11                     this[property] = g[property];
12     };
13 
14     g.clones = [];
15 
16     return g;
17 };

Conclusion

Congratulations. If you read through this entire blog post and understood what I wrote then you now know about prototypal inheritance and why it matters. Thank you for bearing with me. I sincerely hope this blog post was helpful to you. Prototypal inheritance is powerful and deserves more credit than it's given. However most people never see this because prototypal inheritance in JavaScript is masked by the constructor pattern.

Related Articles

  1. Prototypal Inheritance in JavaScript
  2. JavaScript inheritance and the constructor property
  3. Use of .apply() with 'new' operator. Is this possible?
  4. Is JavaScript 's “new” Keyword Considered Harmful?
  5. Is JavaScript 's “new” Keyword Considered Harmful (Part 2)?
  6. Stop Using Constructor Functions in JavaScript
  7. JavaScript Constructor Functions vs Factory Functions
  8. Fluent JavaScript – Three Different Kinds of Prototypal OO
  9. Details of the object model
  10. Prototype-based programming

Comments