3 Different Kinds of Prototypal Inheritance: ES6+ Edition
For more on the disadvantages of class inheritance, watch “Classical Inheritance is Obsolete: How to Think in Prototypal OO”:
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.
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:
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
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.
Not to be confused with functional programming.
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.”
- More expressive
- More flexible
Watch “Composition Over Inheritance” for more.
What About `class`?
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.
Want to learn more about prototypal inheritance and object composition?
He spends most of his time in the San Francisco Bay Area with the most beautiful woman in the world.