Part 1: The JavaScript OLOO Pattern Explained (with pictures)
Having listened to a number of students during my software development studies, I observed a lot of confusion surrounding JavaScript’s Object Creation Patterns. For beginners it can be especially tricky — and without careful study, can lead to hesitation around using objects. With this in mind, I braced myself for a deep dive into learning JavaScript’s Object Creation patterns for the first time.
To my relief, my progress was smooth as I learned these patterns at Launch School — with little to no confusion. How was this done? Well, I am very happy to share my findings here in two parts:
- Part 1 covers Prototype Inheritance and delegation in JavaScript with the OLOO pattern
- Part 2 deals with the Pseudo-classical pattern — a combination of constructor functions and prototypes that create objects in JavaScript
As there is a plethora of technical information out there on JavaScript Objects, we will attempt to keep technicalities light. We will instead take a top-down, integrated approach to these articles. Before I get stuck into JavaScript prototypes, let us first discuss how other OOP languages create objects using classes.
Traditional Class-Based Objects
We will be expanding on Software Engineer Mehdi Maujood’s brilliant chair object metaphor:
In a class-based language, such as Ruby: classes are like blueprints. If we wanted to manufacture a chair for example, that has a width
and height
, we would create a class that serves as the “blueprint” and create (or instantiate) a chair based on that blueprint:
class Chair
def initialize(width, height)
@width = width
@height = height
end
endmy_chair = Chair.new(50, 45)
What if we then wanted to create a more specific object, say a desk chair? We must make changes to the blueprint. Desk chairs are typically more complex than a basic chair — their height can be adjusted and they have wheels. To accommodate both chair objects, we must keep our original Chair
class (superclass) and create a more specific class DeskChair
(subclass), which will set the original properties of our Chair
class using the super
keyword:
class Chair
def initialize(width, min_height)
@width = width
@min_height = min_height
end
endclass DeskChair < Chair
def initialize(width, min_height, max_height, wheels)
super(width, min_height)
@max_height = max_height
@wheels = wheels
end
endmy_chair = Chair.new(50, 45)
my_desk_chair = DeskChair.new(50, 45, 52, 5)
JavaScript Prototypes
Continuing with Mehdi Maujood’s chair metaphor — when it comes to prototypes, we don’t create blueprints or classes, we just create a chair object. Imagine chopping up some wood and building a chair out of it. This is a fully functional chair object and can work as the prototype for future chair objects:
let myChair = {
width: 50,
height: 45,
};
Maujood goes on to explain: “In the world of prototypes, you build a chair and simply make ‘duplicates’ of it”. If we want to build our desk chair, we ‘duplicate’ our original chair using Object.create
, give our new chair some adjustable height, add some wheels to it and ta-da! We have a desk chair! We didn’t have to create a blueprint for that now did we?
let myChair = {
width: 50,
minHeight: 45,
};let myDeskChair = Object.create(myChair);
myDeskChair.maxHeight = 52;
myDeskChair.wheels = 5;
And if we wanted a chair
object of any width
and height
we can simply add an init
method to set initial values for a chair of any dimension:
let myChair = {
init(width, height) {
this.width = width;
this.height = height;
return this;
}
};let anotherChair = Object.create(myChair).init(40,60);
There is a major yet subtle difference between class-based objects and prototypal objects. If we were to compare a Ruby desk chair object to a JavaScript desk chair object, aside from Ruby’s superficial Object id information: #<DeskChair: 0x000055e0cfa1fddc8
, we would find an important distinction. Can you spot it in the code below?
Ruby’s Desk Chair:
p my_desk_chair#<DeskChair:0x000055e0cfa1fdc8
@width=50, @min_height=45, @max_height=52, @wheels=5>
JavaScript‘s Desk Chair:
console.log(myDeskChair);{ maxHeight: 52, wheels: 5 }
To spot the difference here, we first need to understand JavaScript’s Prototypal Chain.
The Prototypal Chain: Inheritance & Delegation
If you looked close enough you might have noticed that the Ruby desk chair contained all its properties: width
, min_height
, max_height
and wheels
whereas our new JavaScript desk chair object only contains maxHeight
and wheels
. That’s strange…didn’t we ‘duplicate’ our original chair? Shouldn’t it have all the properties from our original chair prototype?
Actually….our new desk chair object does have access to all its properties:
console.log(myDeskChair); // { maxHeight: 52, wheels: 5 }myDeskChair.width; // 50
myDeskChair.min_height; // 45
Wait…what’s going on?! Those properties don’t exist on myDeskChair
yet we still retain access to them — how is this possible?
As it turns out, myDeskChair
starts out as a blank object that we added the maxHeight
and wheels
properties to. Every object in JavaScript contains a hidden property called [[Prototype]]
. This property contains access to an object’s prototype. When we created our desk chair, Object.create
sets myDeskChair
's hidden [[Prototype]]
property to reference the myChair
object as its value.
To find the “missing” properties on myDeskChair
, we can access any properties on our object’s prototype using JavaScript’s prototype chain.
When we attempt to access the width
property from myDeskChair
, JavaScript will check the myDeskChair
object first. The width
property is not found so JavaScript will lookup its prototype chain via myDeskChair
‘s hidden [[Prototype]]
property. It’s prototype is myChair
– which JavaScript will search next and find that a width
property exists. It will access and return the first width
property it sees – enabling prototype properties further up the lookup chain that have the same name to be overridden.
At the top of the prototype chain lies the most basic prototype object called Object.prototype
. All objects will have this object as their prototype by default. Object.prototype
does not have a prototype of its own and will return null
if we attempt to access its prototype:
This approach is known as prototypal inheritance or delegation. This is where newly created objects can delegate messages up its prototype chain to one of its prototypes, in order to retrieve the requested property or method. This keeps future objects small and efficient, containing only the necessary properties it needs that make it unique from other linked objects.
Conclusion
This Object creation pattern is known as OLOO or “Objects Linking to Other Objects”, which is at the heart of how objects really work in JavaScript. Historically speaking OLOO was introduced after the Pseudo-classical pattern, in an attempt to return to the true nature of JavaScript objects, prototypes and delegation.
The Pseudo-classical Object creation pattern however was the original way of creating objects in JavaScript and is a valid pattern in its own right. In Part 2, we will discuss the breakdown of how it works and how objects are secretly working behind the scenes!