Object Composition in Javascript

Michael Ries
Oct 1, 2017 · 5 min read

You’ve just landed your dream job as a game developer, building a role-playing game with mages and fighters. You’re comfortable with object oriented programming from your experience with Java and Ruby, but the new project is in Javascript. Fortunately you read last week’s post on Javascript ‘Classes’ and Prototypal Inheritance and you’re ready to dive in. You design your class structure and get started.

Our RPG characters using Classes

A few months later you release your game and it’s an overnight success! Players love it, but after a while they become bored with just mages and fighters and they demand a new player type, ‘paladin’, that can cast spells and fight.

The Problems of Classical Inheritance

You look at your class structure and ponder how to implement the paladin. You consider a couple of options:

  1. You could write a new Paladin class that extends from Character and then copy the fight() and cast() code from Fighter and Mage, but then you’d be duplicating code and you know that’s not a good solution.
  2. You could move fight() and cast() code up into the Character class so that all three character types could use them. This is perhaps a better solution, but you’ll also need to override the fight() function in the Mage class and the cast() function in Fighter class.

Neither looks like a very good solution… and why are you having to muck about in code that’s working perfectly fine to add the Paladin?

You’ve just discovered some of the problems with classical inheritance.

  • You have to define your class taxonomy in advance. The is just about impossible to get right the first time, except for trivial projects.
  • If you don’t get it right, you’ll be forced to change it later. And unfortunately, the parents and the children are tightly coupled which requires you to make changes in many places, and probably add bugs.

These problems are so common that there are names for them. Option 1 is known as the duplication by necessity problem. Option 2 is called the Gorilla / Banana problem.

The problem with object-oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.

— Joe Armstrong, creator of Erlang

The problem isn’t really with object-oriented languages. It’s with classical inheritance, which makes you think in terms of what things are rather than what they do.

Object Composition in Javascript

There are three primary methods for inheritance in Javascript. The first, prototype delegation, is the method we used to mimic class inheritance in the example above. The other two are concatenative inheritance and functional inheritance. Let’s use these to rewrite our characters without a concept of class.

RPG Characters using Object Composition

In this example, we focus on what an object can do rather than trying to define what it is. We define behaviors using factory functions that accept state and return an object that acts upon it.

Our canCast() function accepts state and returns an object with the cast() function. When we execute cast(‘fireball’) the function logs to the console and reduces the mana by 1. Note that we could have many functions on the object returned by canCast(), perhaps one for each spell, but it’s more future-safe to have only one.

We create our mage with another factory function. In this case, we define our state with some initial values, and then we use Object.assign() to create and return a new object that includes the properties from state and also the cast() function. This copying of properties from one or more objects into a new one is known as concatenative inheritance.

Our brand new paladin can be created like this:

Roland, our new Paladin

Let the gamers rejoice!

`new` and `this`

You may have noticed one difference between object composition and classical inheritance that I haven’t mentioned. In classical inheritance, you create a new object using the keyword, but you don’t use with factory functions. This is another advantage that object composition has. When using classical inheritance, it is easy to forget the when instantiating an object and Javascript doesn’t warn you if you do. Instead it will execute your constructor function as a regular function. This can lead to difficult to diagnose bugs.

Additionally, you probably noticed that the object composition approach completely avoided the use of . The keyword is confusing to many, and doesn’t behave quite the same way as it does in other languages, so avoiding it helps in code comprehension.

I should point out here that avoiding comes at a cost. Because Object.assign() copies the properties (including functions) from one object to another, you are increasing the memory burden. When you use prototype delegation and the keyword you don’t duplicate the functions and properties, you delegate instead. That’s the value of prototypal inheritance, but it doesn’t mean you have to emulate classes. I’ll be exploring this in a future article.

Summary

  • In classical inheritance, we tend to think of our objects in terms of what they are, but when using object composition we think about what they can do.
  • Classical inheritance is difficult to do correctly, and difficult to change later.
  • Javascript provides simple tools for object composition that avoid the pitfalls of classical inheritance.

Parting words

It may seem as though I’ve been bashing pretty hard on classical languages in my last couple of posts. If you’ve done much OOP you already know you should prefer object composition over inheritance, and many classical languages address this need through interfaces or modules. Unfortunately, we are nearly all taught about classes first and interfaces and object composition later. Why?

And to be fair, creating objects using constructor functions and prototype delegation is faster than factory functions, but the difference is so small that unless you are creating 10s of thousands of objects per second, it won’t have a significant impact.

I encourage you to play with the code above by copying it to repl.it

See if you can add an Archer to the party, or modify the behaviors so that they can’t be used too often!

Also, please check out my other posts:

Javascript ‘Classes’ and Prototypal Inheritance

Understanding Closures in Javascript

Form Validation in React

OAuth in React/Redux Applications

Javascript: What’s This?

Resources

Master the JavaScript Interview: What’s the Difference Between Class & Prototypal Inheritance?

Why Extends is Evil

Goodbye, Object Oriented Programming

Code Monkey

Musings of a Full Stack Web Developer

Michael Ries

Written by

Solving complex problems one simple problem at a time.

Code Monkey

Musings of a Full Stack Web Developer

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade