Javascript | Composition vs Inheritance

Many developers begin their journey with an Object-Oriented Programming (OOP) language such as Ruby or Python. These new developers soon find out that a core concept in OOP is classes. When shifting over to Javascript, a Functional Programming (FP) language, the natural instinct is to use the same programming paradigm that was used in OOP. Javascript, and its prototype chain, make an inheritance-based architecture possible.

I will be doing a deep dive into an alternative to inheritance called composition. This approach makes use of the fact that in Javascript, functions are first-class objects. Composition is a highly flexible and modular architecture that many experts believe should in almost all instances be chosen over inheritance.

Inheritance | A Logical First Step

Inheritance, as the name suggests, is a programming paradigm that heavily borrows from its namesake in Biology. In the same way species have a well-defined relationship so do classes in OOP.

In Biology you could construct an evolutionary tree where you have animals at the top of the chain. From animals you could branch to mammals which branches to humans which branches to you (I’m assuming you’re a human and not a Labrador with a penchant for Javascript).

Some properties belong to each of those items in the chain. We could say that all animals breathe air . There are, however, some properties not shared by all. For example, not all animals can play the trombone. Not all mammals can play the trombone either. It is only once we hit the branch for humans that the trombone-playing characteristic becomes relevant.

You may have noticed the pattern that inheritance is top down. Animals have the characteristic of breathing air and therefore all items in the chain below it also breathe air. Humans can play the trombone and therefore everything below it also has the potential to play the trombone (yes you — time to start those lessons). This is exactly how prototypal inheritance works in Javascript. As you’ll see in the code below the property for breathes is available to all objects yet trombone is only available to human and you.

Inheritance is fantastic because it uses a methodology for defining relationships that we are already familiar with. Conceptually it is easily understandable and relationships are highly structured and predictable.

Some Downsides to Inheritance

Everything in the code above is peachy until one day you turn on your TV to see a news report about a dolphin that can play the trombone. Your first instinct is probably to think what a crazy, amazing world we live in. Then you realize that this has completely ruined your inheritance structure. We now have a second mammal that can also play the trombone.

Let’s start off by thinking where the dolphin lives in the prototype chain. If we inserted it below human it would then have access to the trombone property. That problem would be solved but others would be created as now the dolphin would also have access to the rest of the human’s unique properties such as their ability to read, write & construct buildings. A possible way to fix this is to add a new object to the prototype chain below mammals, e.g. mammals that play music. We could move the trombone-playing property to this object rather than defining it in human. Dolphin and human can now both inherit the trombone property from this object.

It looks really messy but at least our problem is solved. That is until we discover an alligator than can play the trombone. An alligator is a reptile, not a mammal, solving this problem is starting to become an enormous mess!

Now we can start to see the problem with inheritance. All is well and good when the objects and their properties are orderly and predictable. In life we cannot predict the future. Models can evolve such that their relationships can completely change over time. We need an approach where changing a model’s properties is more flexible. This brings us to the concept of composition.

Composition | A Flexible Solution

Another downside of inheritance, that we haven’t discussed yet, is the excess baggage it tends to weigh down on objects. Objects don’t only inherit their immediate parent’s properties— they inherit all of the properties from all of the objects in the prototype chain above them. The animal, mammal and human objects may each have hundreds of properties associated with them. When we define you we probably only really need a handful of them. Having all of those excess, unused properties is unnecessary bloat which slows your code down. Wouldn’t it be great if we could define objects such that we could hand-pick the properties that best describe them? That is exactly what composition offers.

Composition combines the power of first-class functions and factory functions. First-class functions means that Javascript can pass functions in as arguments to other functions. It also means that functions can be returned as results from other functions. Factory functions are simply functions that return an object. We can re-define the inheritance-based approach above such that the properties for breathes and trombone are stand-alone functions. We can then bind the relevant characteristics on to each individual object definition. I’ve added properties for land/sea bound to make the concept clearer.

If we somewhere down the line discover an Amoeba that can play the trombone, construct buildings and is well-versed in Russian literature, creating this object using composition will be no problem at all. This is the magic of using composition rather than inheritance.

Full-Stack Web Developer | www.jordaneckowitz.com