3 Different Kinds of Prototypal Inheritance: ES6+ Edition

Eric Elliott
May 30, 2016 · 5 min read
Image for post
Image for post
Triplets — Phil Dolby (CC-BY-2.0)

This article was adapted from “Programming JavaScript Applications”, expanded, and updated for ES6.

In order to claim fluency in JavaScript, it’s important to understand how JavaScript’s native inheritance capabilities work. This is an often neglected area of JavaScript writing and learning, but understanding it can be dramatically empowering.

JavaScript is one of the most expressive programming languages ever created. One of its best features is the ability to create and inherit from objects without classes and class inheritance.

Its combination of delegate prototypes, runtime object extension, and closures allow you to express three distinct kinds of prototypes in JavaScript, which provide significant advantages over class inheritance.

For more on the disadvantages of class inheritance, watch “Classical Inheritance is Obsolete: How to Think in Prototypal OO”:

Recorded at the O’Reilly Fluent conference, 2013

Delegation / Differential Inheritance

A delegate prototype is an object that serves as a base for another object. When you inherit from a delegate prototype, the new object gets a reference to the prototype.

When you try to access a property on the new object, it checks the object’s own properties first. If it doesn’t find it there, it checks the `[[Prototype]]`, and so on up the prototype chain until it gets back to `Object.prototype`, which is the root delegate for most objects.

Image for post
Image for post
The Prototype Chain

Method delegation can preserve memory resources because you only need one copy of each method to be shared by all instances. There are several ways to set up the relationship in JavaScript. The one you’re likely to see in ES6 goes something like this:

Due to the many problems associated with class inheritance, and the temptation to extend classes, I don’t recommend this technique. I present it here only because it’s likely to be a familiar point of reference.

You may also be familiar with the ES5 constructor function version:

I prefer a factory function using `Object.create()`. (In JavaScript, any function can create new objects. When it’s not a constructor function, it’s called a factory function):

You can avoid property delegation by setting the prototype to `null` using `Object.create(null)`.

One major drawback to delegation is that it’s not very good for storing state. If you try to store state as objects or arrays, mutating any member of the object or array will mutate the member for every instance that shares the prototype. In order to preserve instance safety, you need to make a copy of the state for each object.

Concatenative Inheritance / Cloning / Mixins

Concatenative inheritance is the process of copying the properties from one object to another, without retaining a reference between the two objects. It relies on JavaScript’s dynamic object extension feature.

Cloning is a great way to store default state for objects: This process is commonly achieved using `Object.assign()`. Prior to ES6, it was common to use similar `.extend()` methods from Lodash, Underscore, or jQuery.

It’s common to see this style used for mixins. For example, you can turn any object into an event emitter by mixing in an `EventEmitter3` prototype:

We can use this to create a Backbone-style event emitting model:

Concatenative inheritance is very powerful, but it gets even better when you combine it with closures.

Functional Inheritance

Not to be confused with functional programming.

Coined by Douglas Crockford in “JavaScript: The Good Parts”. Functional inheritance makes use of a factory function, and then tacks on new properties using concatenative inheritance.

Functions created for the purpose of extending existing objects are commonly referred to as functional mixins. The primary advantage of using functions for extension is that it allows you to use the function closure to encapsulate private data. In other words, you can enforce private state.

It’s a bit awkward to hang the attributes on a public property where a user could set or get them without calling the proper methods. What we really want to do is hide the attributes in a private closure (See also: “What is a Closure?”). It looks something like this:

By moving `attrs` from a public property to a private identifier, we remove all trace of it from the public API. The only way to use it now is via the privileged methods. Privileged methods are any methods defined within the closure’s function scope, which gives them access to the private data.

Note in the example above, we have the `mixinModel()` wrapper around the actual functional mixin, `rawMixin()`. The reason we need that is because we need to set the value of `this` inside the function, which we do with `Function.prototype.call()`. We could skip the wrapper and let callers do that instead, but that would be obnoxious.

Composition Over Class Inheritance

“Favor object composition over class inheritance.” ~ The Gang of Four, “Design Patterns: Elements of Reusable Object Oriented Software

Class inheritance creates is-a relationships with restrictive taxonomies, all of which are eventually wrong for new use-cases. But it turns out, we usually employ inheritance for has-a, uses-a, or can-do relationships.

Composition is more like a guitar effects pedalboard. Want something that can do delay, subtle distortion, and a robot voice? No problem! Just plug them all in:

const effect = compose(delay, distortion, robovoice); // Rock on!

When would you want to use class inheritance? For me, the answer is simple: “Never.”

Composition is:

  • Simpler
  • More expressive
  • More flexible

Watch “Composition Over Inheritance” for more.

What About `class`?

As you can see, JavaScript provides a very flexible object system without the need to rely on `class`. So why did we add `class` in the first place? Because a lot of people are familiar with the class paradigm from other languages, and people kept trying to emulate it in JavaScript.

Inheritance in JavaScript is so easy, it confuses people who expect it to take effort. To make it harder, we added `class`.

Several popular libraries implemented pseudo-class inheritance in JavaScript using the delegate prototype chain to emulate class inheritance. Adding an official `class` keyword provided a single canonical way to emulate class inheritance in JavaScript — but in my opinion, you should avoid it altogether. In JavaScript, composition is simpler, more expressive, and more flexible than class inheritance. I can’t think of a single good use case where `class` is a better fit than the native prototypal alternatives.

I’ve been challenging people to come up with a compelling use-case for class inheritance for several years, and instead of good use-cases, I hear a whole lot of common misconceptions.

Conclusion

Once you start thinking in terms of class-free objects and inheritance using prototypes, and concatenation, you begin to really appreciate how simple, powerful, and flexible JavaScript’s object system can be.

Want to learn more about prototypal inheritance and object composition?

Learn JavaScript with Eric Elliott

Eric Elliott is the author of “Programming JavaScript Applications” (O’Reilly), and “Learn JavaScript with Eric Elliott”. He has contributed to software experiences for Adobe Systems, Zumba Fitness, The Wall Street Journal, ESPN, BBC, and top recording artists including Usher, Frank Ocean, Metallica, and many more.

He spends most of his time in the San Francisco Bay Area with the most beautiful woman in the world.

JavaScript Scene

JavaScript, software leadership, software development, and…

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store