3 Different Kinds of Prototypal Inheritance: ES6+ Edition

Eric Elliott
May 30, 2016 · 8 min read
Image for post
Image for post
Triplets — Phil Dolby (CC-BY-2.0)
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.

Image for post
Image for post
The Prototype Chain

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.

Functional Inheritance

Not to be confused with functional programming.

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.

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

Stamps

Stamps are composable factory functions. I created stamps as an experiment for the O’Reilly book, “Programming JavaScript Applications”. It all started with this interesting challenge:

Descriptor

Composable descriptor (or just descriptor) is a meta data object which contains the information necessary to create an object instance.

  • `properties` — A set of properties that will be added to new object instances by assignment.
  • `initializers` — An array of functions that will run in sequence. Stamp details and arguments get passed to initializers.
  • `staticProperties` — A set of static properties that will be copied by assignment to the stamp.

Why Stamps?

Prototypal inheritance is great, and JavaScript’s capabilities give us some really powerful tools to explore it, but it could be easier to use.

  • `init(…functions: […Function]) => Stamp` takes any number of initializer functions and returns a new stamp.

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.

Conclusion

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


Learn JavaScript with Eric Elliott


JavaScript Scene

JavaScript, software leadership, software development, and…

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