This book is a work in progress. Content may be updated as the source material evolves.
this keyword. But we’re now going to revisit what we’ve learned so far from a bit of a different perspective.
What if you could leverage all the power of the objects, prototypes, and dynamic this mechanisms together, without ever using class or any of its descendants?
In fact, I would argue JS is inherently less class-oriented than the class keyword might appear. Because JS is a dynamic, prototypal language, its strong suit is actually… delegation.
Preamble
Before we begin looking at delegation, I want to offer a word of caution. This perspective on JS’s object[[Prototype]] and this function context mechanisms is not mainstream. It’s not how framework authors and libraries utilize JS. You won’t, to my knowledge, find any big apps out there using this pattern.
So why on earth would I devote a chapter to such a pattern, if it’s so unpopular?
Good question. The cheeky answer is: because it’s my book and I can do what I feel like!
But the deeper answer is, because I think developing this understanding of one of the language’s core pillars helps you even if all you ever do is use class-style JS patterns.
To be clear, delegation is not my invention. It’s been around as a design pattern for decades. For the purposes of this chapter, I’m going to present delegation, as implemented via JS mechanics, as an alternative design pattern, positioned somewhere between class-orientation and object-closure/module patterns.
class mechanism down to its individual parts. Then we’ll cherry-pick and mix the parts a bit differently.
What’s A Constructor, Anyway?
In Chapter 3, we sawconstructor(..) as the main entry point for construction of a class instance. But the constructor(..) doesn’t actually do any creation work, it’s only initialization work. In other words, the instance is already created by the time the constructor(..) runs and initializes it — e.g., this.whatever types of assignments.
So where does the creation work actually happen? In the new operator. As the section “New Context Invocation” in Chapter 4 explains, there are four steps the new keyword performs; the first of those is the creation of a new empty object (the instance). The constructor(..) isn’t even invoked until step 3 of new’s efforts.
But new is not the only — or perhaps even, best — way to create an object “instance”. Consider:
class, just a regular function definition (Point2d(..)). There’s no new invocation, just a regular function call (Point2d(3,4)). And there’s no this references, just regular object property assignments (instance.x = ..).
The term that’s most often used to refer to this pattern of code is that Point2d(..) here is a factory function. Invoking it causes the construction (creation and initialization) of an object, and returns that back to us. That’s an extremely common pattern, at least as common as class-oriented code.
Factory Initialization
Let’s evolve this pattern step by step:instance.init(..) call makes use of the [[Prototype]] linkage set up via __proto__ assignment. Thus, it delegates up the prototype chain to prototypeObj.init(..), and invokes it with a this context of instance — via implicit context assignment (see Chapter 4).
Let’s continue the deconstruction. Get ready for a switcheroo!
Point2d(..) function, and instead renamed the prototypeObj as Point2d. Weird.
But let’s look at the rest of the code now:
Object.create(..):
Object.create(..) perform?
If those look familiar, it’s because those are exactly the same first two steps of the
new keyword (see Chapter 4).
Let’s put this back together now:
class approach?
This pattern ditches the class and new keywords, but accomplishes the exact same outcome. The cost? The single new operation was broken up into two statements: Object.create(Point2d) and point.init(3,4).
Help Me Reconstruct!
If having those two operations separate bothers you — is it too deconstructed!? — they can always be recombined in a little factory helper:Ditching Class Thinking
Quite frankly, the deconstruction we just went through only ends up in slightly different, and maybe slightly better or slightly worse, code as compared to theclass style. If that’s all delegation was about, it probably wouldn’t even be useful enough for more than a footnote, much less a whole chapter.
But here’s where we’re going to really start pushing the class-oriented thinking itself, not just the syntax, aside.
Class-oriented design inherently creates a hierarchy of classification, meaning how we divide up and group characteristics, and then stack them vertically in an inheritance chain. Moreover, defining a subclass is a specialization of the generalized base class. Instantiating is a specialization of the generalized class.
Behavior in a traditional class hierarchy is a vertical composition through the layers of the inheritance chain. Attempts have been made over the decades, and even become rather popular at times, to flatten out deep hierarchies of inheritance, and favor a more horizontal composition through mixins and related ideas.
For the rest of this chapter, I intend to discard both the syntax of
class and the thinking of class.Delegation Illustrated
So what is delegation about? At its core, it’s about two or more things sharing the effort of completing a task. Instead of defining aPoint2d general parent thing that represents shared behavior that a set of one or more child point / anotherPoint things inherit from, delegation moves us to building our program with discrete peer things that cooperate with each other.
I’ll sketch that out in some code:
Coordinates as a concrete object that holds some behaviors I associate with setting point coordinates (x and y). I’ve also defined Inspect as a concrete object that holds some debug inspection logic, such as toString().
I then create two more concrete objects, point and anotherPoint.
point has no specific [[Prototype]] (default: Object.prototype). Using explicit context assignment (see Chapter 4), I invoke the Coordinates.setXY(..) and Inspect.toString() utilities in the context of point. That is what I call explicit delegation.
anotherPoint is [[Prototype]] linked to Coordinates, mostly for a bit of convenience. That lets me use implicit context assignment with anotherPoint.setXY(..). But I can still explicitly share anotherPoint as context for the Inspect.toString() call. That’s what I call implicit delegation.
The point here is: none of these four objects is a parent or child. They’re all peers of each other, and they all have different purposes. We can organize our behavior in logical chunks (on each respective object), and share the context via this (and, optionally [[Prototype]] linkage), which ends up with the same composition outcomes as the other patterns we’ve examined thus far in the book.
That is the heart of the delegation pattern, as JS embodies it.
In the first edition of this book series, this book (“this & Object Prototypes”) coined a term, “OLOO”, which stands for “Objects Linked to Other Objects” — to stand in contrast to “OO” (“Object Oriented”). In this preceding example, you can see the essence of OLOO: all we have are objects, linked to and cooperating with, other objects. I find this beautiful in its simplicity.
Composing Peer Objects
Let’s take this delegation even further. In the preceding snippet,point and anotherPoint merely held data, and the behaviors they delegated to were on other objects (Coordinates and Inspect). But we can add behaviors directly to any of the objects in a delegation chain, and those behaviors can even interact with each other, all through the magic of virtual composition (this context sharing).
To illustrate, we’ll evolve our current point example a fair bit. And as a bonus we’ll actually draw our points on a <canvas> element in the DOM. Let’s take a look:
Canvas and ControlPoint) alongside the previous Coordinates object.
Make sure you see and understand the interactions between these three concrete objects.
ControlPoint is linked (via __proto__) to implicitly delegate ([[Prototype]] chain) to Coordinates.
Here’s an explicit delegation: Canvas.setOrigin.call(ControlPoint,100,100); I’m invoking the Canvas.setOrigin(..) call in the context of ControlPoint. That has the effect of sharing ctx with setOrigin(..), via this.
ControlPoint.setXY(..) delegates implicitly to Coordinates.setXY(..), but still in the context of ControlPoint. Here’s a key detail that’s easy to miss: see the this.render() inside of Coordinates.setXY(..)? Where does that come from? Since the this context is ControlPoint (not Coordinates), it’s invoking ControlPoint.render().
ControlPoint.render() explicitly delegates to Canvas.renderScene(), again still in the ControlPoint context. renderScene() calls this.draw(), but where does that come from? Yep, still from ControlPoint (via this context).
All three objects have methods that end up invoking each other. But these calls aren’t particularly hard-wired. Canvas.renderScene() doesn’t call ControlPoint.draw(), it calls this.draw(). That’s important, because it means that Canvas.renderScene() is more flexible to use in a different this context — e.g., against another kind of point object besides ControlPoint.
It’s through the
this context, and the [[Prototype]] chain, that these three objects basically are mixed (composed) virtually together, as needed at each step, so that they work together as if they’re one object rather than three seperate objects.That’s the beauty of virtual composition as realized by the delegation pattern in JS.Flexible Context
I mentioned above that we can pretty easily add other concrete objects into the mix. Here’s an example:this context tends to make testing different parts of the program independently, somewhat easier.
For example, Object.setPrototypeOf(..) can be used to dynamically change the [[Prototype]] linkage of an object, delegating it to a different object such as a mock object. Or you could dynamically redefine GuideLine.draw() and GuideLine.render() to explicitly delegate to a MockCanvas instead of Canvas.
The this keyword, and the [[Prototype]] link, are a tremendously flexible mechanism when you understand and leverage them fully.
Why This?
OK, so it’s hopefully clear that the delegation pattern leans heavily on implicit input, sharing context viathis rather than through an explicit parameter.
You might rightly ask, why not just always pass around that context explicitly? We can certainly do so, but… to manually pass along the necessary context, we’ll have to change pretty much every single function signature, and any corresponding call-sites.
Let’s revisit the earlier ControlPoint delegation example, and implement it without any delegation-oriented this context sharing. Pay careful attention to the differences:
[[Prototype]] entirely, and only relies on far fewer basic this.-style references to properties and methods.
By contrast, the delegation style I’m advocating for in this chapter is unfamiliar and uses [[Prototype]] and this sharing in ways you’re not likely familiar with. To use such a style effectively, you’ll have to invest the time and practice to build a deeper familiarity.
Summary
Delegation is a powerful alternative design pattern to class-orientation. Instead of organizing code in parent-child hierarchies with vertical inheritance, delegation uses peer objects that cooperate through[[Prototype]] linkage and this context sharing.
Key takeaways:
- Objects as peers: Delegation treats objects as equals that cooperate, rather than parents and children in an inheritance hierarchy
- Virtual composition: Behaviors are composed at runtime through
thiscontext sharing, rather than at author-time through class hierarchies - OLOO pattern: “Objects Linked to Other Objects” emphasizes the simplicity of objects cooperating with other objects
- Flexibility: The
thiskeyword and[[Prototype]]chain provide tremendous flexibility for dynamic behavior composition

