Introduction
to the Path of Summoning and Commanding Objects (a.k.a. Object Oriented Programming)
Many ways to build a Golem there are,
cast its blueprint in clay
then recite the instantiation chants,or put together the parts
that'll made the whole alive,or bring it forth at once
with no prior thought required.Many ways to build a Golem there are,
in JavaScript.
Let me Tell You About OOP in JavaScript
Welcome to the Path of Summoning 1 and Commanding Objects! In this part of this ancient manuscript you’ll learn how you can work with objects in JavaScript, how to define them, create them and even how to interweave them. By the end of it you’ll have mastered Object Oriented Programming in JavaScript and you’ll be ready to command your vast armies of objects into glory.
JavaScript OOP story is pretty special. When I started working seriously with JavaScript some years ago, one of my first concerns as a C# developer coming to JavaScript was to find out how to write a class. I had a lot of prowess in C# and I wanted to bring all that knowledge and ability into the world of JavaScript, so my first approach was to try to map every C# concept into JavaScript. I saw classes, which are such a core construct in C# and which were such an important part of my programming style at the time, as my secret passage to being proficient in JavaScript.
Well, it took me a long while to understand how to mimic classical inheritance in JavaScript. But it was time well spent because, along the way, I learnt a lot about JavaScript and about the many different ways in which it supports object-oriented programming. This quest helped me look beyond classical inheritance into other OOP styles more adequate to JavaScript where flexibility and expressiveness reign supreme over the strict and fixed taxonomies of classes.
In this part of the series I will attempt to bring you with me through the same journey that I experienced. We will start with how to achieve classical inheritance in JavaScript, so you can get a basic level of proficiency by translating your C# skills into JavaScript. And then we will move beyond that into new patterns that truly leverage JavaScript as a language and which will blow your mind.
Let’s get a taste of what is in store for you by getting a high level overview 2 of object-oriented programming in JavaScript. If you feel like you can’t follow the examples don’t worry. For in the upcoming chapters we will dive deeper into each of the concepts and techniques used, and we will discuss them separately and at a much slower pace.
C# Classes in JavaScript
A C# class is more or less equivalent to a JavaScript constructor function and prototype pair:
The constructor function represents how an object should be constructed (created) while the prototype represents a piece of reusable behavior. In practice, the constructor function usually defines the data members within a “class” while the prototype defines its methods.
You can instantiate a new Minion
object by using the new
operator on the constructor function:
As a result of instantiating an orc
we get a new Minion
object with two properties hp
and name
. The Minion
object also has a hidden property called [[prototype]]
that points to its prototype
which is an object that has a method toString
. This prototype and its toString
method are shared across all instances of the Minion
class.
When you call orc.toString
the JavaScript runtime checks whether or not the orc
object has a toString
method and if it can’t find it, like in this case, it goes down the prototype chain until it does. The prototype chain is established by the object itself, its prototype, its prototype’s prototype and so on. In this case, the prototype chain leads to the Minion.prototype
object that has a toString
method. This method will then be called and evaluated as this.name
(orc
).
We can mimic classical inheritance by defining a new “class” Wizard
and make it inherit from Minion
:
We achieve classical inheritance by:
- calling the
Minion
constructor function from theWizard
constructor - assigning a
Minion
object (created viaObject.create
) as prototype of theWizard
“class”
With the constructor delegation we ensure that a Wizard
object has all the properties of a Minion
object. While with the prototype chain we ensure that all the methods in the Minion
prototype are available to a Wizard
object.
We can also augment the Wizard
prototype with new methods:
Or even override or extend existing methods within its base “class” Minion
:
Finally, we can verify that everything works as expected by instantiating our very own powerful wizard:
As you can see from these previous examples, writing “classes” prior to ES6 was no easy feat. It required a lot of moving components and a lot of code. That’s why ES6 brings classes along which provide a much nicer syntax to what you’ve seen thus far. This means that instead of having to handle constructor functions and prototypes yourself, you get the new class
keyword that nicely wraps both into a more coherent and developer friendly syntax:
You also get access to the extend
and super
keywords, where extend
lets you establish class inheritance and super
lets you access methods from parent classes:
Again, we can verify that it works just like it did before by instantiating a classy wizard:
It is important to highlight though that ES6 classes are just syntactic sugar. Under the hood, these ES6 classes that you have just seen are translated into constructor function/prototype pairs.
And that is how you mimic classical inheritance in JavaScript. Let’s now look beyond.
OOP Beyond Classes
There are a lot of people in the JavaScript community that claim that the cause of JavaScript not having a nice way to mimic classical inheritance, not having classes, is that you were not meant to use them in the first place. You were meant to embrace prototypical inheritance, the natural way of working with inheritance in JavaScript, instead of hacking it to make it behave sort of like classical inheritance.
In the world of prototypical inheritance you only have objects, and particularly objects that are based upon other objects which we call prototypes. Prototypes lend behaviors to other objects by means of delegation (via the prototype chain) or by the so called concatenative inheritance which consists in copying behaviors.
Let’s illustrate the usefulness of this type of inheritance with an example. Imagine that, in addition to wizards, we also need to have some thieves for those occassions when we need to use more gentle/shrew hand against our enemies. A ClassyThief
class could look something like this:
And let’s say that a couple of weeks from now, we realize that it would be nice to have yet another type of minion, one that can both cast spells and steals, and why not? Play some music. Something like a Bard. In pseudo-code we would describe it as follows:
Well, we’ve put ourselves in a pickle here. Classical inheritance tends to build rigid taxonomies of types where something is a Wizard
, something is a Thief
but it cannot be both. How would we solve the issue of the Bard
using classical inheritance in C#? Well…
- We could move both
castsSpell
andsteals
methods to a base classSpellCastingAndStealingMinion
that all three types could inherit. TheClassyThief
would throw an exception when casting spell and so would theClassyWizard
when stealing. Not a very good solution (goodbye Liskov principle 3) - We could create a
SpellCastingAndStealingMinion
that duplicates the functionality inClassyThief
andClassyWizard
and make theBard
inherit from it. This solution would imply code duplication and thus additional maintenance. - We could define interfaces for these behaviors
ICanSteal
,ICanCastSpells
and make each class implement these interfaces. Nicer but we would need to provide an specific implementation in each separate class. No so much code reuse here.
So none of these solutions are very attractive: They involve bad design, code duplication or both. Can JavaScript helps us to achieve a better solution to this problem? Yes! It can!
Imagine that we broke down all these behaviors and encapsulated them inside separate objects (canCastSpells
, canSteal
and canPlayMusic
):
Now that we have encapsulated each behavior in a separate object we can compose them together to provide the necessary functionality to a wizard
, a thief
and a bard
:
And in a very expressive way we can see how a wizard
is someone than can cast spells, a thief
is someone that can steal and a bard
someone that not only can cast spells and steal but can also play music. By stepping out of the rigid limits of classical inheritance and strong and static typing, we get to a place where we can easily reuse behaviors and compose new objects in a very flexible and extensible manner.
We can verify that indeed this approach works beautifully:
The Object.assign
in the examples is an ES6 method that lets you extend an object with other objects. This is effectively the concatenative prototypical inheritance we mentioned previously.
This object composition technique constitutes a very interesting and flexible approach to object-oriented programming that isn’t available in C#. But in JavaScript we can use it even with ES6 classes!
Combining Classes with Object Composition
Do you remember that ES6 classes are just syntactic sugar over the existing prototypical inheritance model? They may look like classical inheritance but they are not. This means that the following mix of ES6 classes and object composition would work:
In this example we extend the ClassyBard
prototype with new functionality that will be shared by all future instances of ClassyBard
. If we instantiate a new bard we can verify that it can steal, cast spells and play music:
This is an example of delegation-based prototypical inheritance in which methods such as steals
, castsSpell
and playsMusic
are delegated to a single prototype object (instead of being appended to each object).
So far you’ve seen classical inheritance mimicked in JavaScript, ES6 classes and object composition via mixin objects, but there’s much more to learn and in greater detail! Take a sneak peak at what you’ll learn in each of the upcoming chapters.
The Path of the Object Summoner Step by Step
In Summoning Fundamentals: an Introduction to Object Oriented Programming in JavaScript you’ll start by understanding the basic constructs needed to define and instantiate objects in JavaScript. In thischapter, constructor functions and the new
operator will join what you’ve discovered thus far about object initializers. You’ll review how to achieve information hiding, you’ll learn the basics of JavaScript’s prototypical inheritance model and how you can use it to reuse code/behaviors and improve your memory footprint. You’ll complete the foundations of JavaScript OOP by understanding how JavaScript achieves polymorphism.
In White Tower Summoning or Emulating Classical Inheritance in JavaScript you’ll use constructor functions in conjunction with prototypes to create the equivalent of C# classes in JavaScript. You’ll then push the boundaries of JavaScript inheritance model further and emulate C# classical inheritance building inheritance chains with method extension and overriding just like in C#.
In White Tower Summoning Enhanced: the Marvels of ES6 Classes you’ll learn about the new ES6 Class syntax and how it provides a much better class development experience over what it was possible prior to ES6.
In Black Tower Summoning: Objects Interweaving Objects with Mixins we’ll go beyond classical inheritance into the arcane realm of object composition with mixins. You’ll learn about the extreme extensibility of object-oriented programming based on object composition. How you can define small pieces of reusable behavior and properties and combine them together to create powerful objects (effectively achieving multiple inheritance).
In **Black Tower Summoning: Safer Object Composition with Traits ** you’ll learn about an object composition alternative to mixins called traits. Traits are as reusable and composable as mixins but are even more flexible and safe as they let you define required properties and resolve conflicts.
In *Black Tower Summoning Enhanced: Next Level Object Composition With Stamps ** you’ll find out about a new way to work with objects in JavaScript called *Stamps that brings object composability to the next level.
Finally, you’ll dive into the depths of Object Internals and discover the mysteries of the low level JavaScript Object
APIs, ES6 proxies and the new ES6 Reflection
APIs.
Concluding
JavaScript is a very versatile language that supports a lot of programming paradigms and different styles of Object-Oriented Programming. In the next chapters you’ll see how you can combine a small number of primitive constructs and techniques to achieve a variety of OOP styles.
JavaScript, like in any other part of the language, gives you a lot of freedom when working with objects, and sometimes you’ll feel like there are so many options and things you can do that you won’t know what’s the right path. Because of that, I’ll try to provide you with as much guidance as I can and highlight the strengths and weaknesses of each of the options available.
Get ready to learn some JavaScript OOP!
-
In Fantasy, Wizards of all sorts and kinds summon or call forth creatures to act as servants, or warriors, and follow the wizard’s commands. As a JavaScript-mancer you’ll be able to use Object Oriented Programming to summon your own objects into reality and do with them as you please. ↩
-
In this section I am going to make a lot of generalizations and simplifications in order to give a simple and clear introduction to OOP in JavaScript. I’ll dive into each concept in greater detail and with an appropriate level of correctness in the rest of the chapters about OOP. ↩
-
The Liskov substitution principle is one of the S.O.L.I.D. principles of object-oriented design. It states that derived classes must be substitutable for their base classes. This means that a derived class should behave as portrayed by its base class and not break the expectations created by its interface. In this particular example if you have a
castsSpell
and asteals
method in the base class, and a derived class throws an exception when you call them you are violating this principle. That’s because the derived class breaks the expectations established by the base class (i.e. that you should be able to use both methods). ↩