Javascript without the this

Using closures in a different way

One of JavaScript’s many wrinkles is the way that this works. It can be quite confusing, since the semantics are quite different from the purely lexical scoping rules which apply for regular variables in JavaScript. What this references can often be totally unrelated to the lexical scope of a function. To work around that we often see tricks like:

Anyone who’s done much JavaScript development has felt this pain. Imagine if that was never needed. How could we get there? Well, one way would be to just never use this. Sounds crazy? Let’s see.

Why this?

Our motivation to use this is usually associated with one of the most useful abstractions in the Object-Oriented paradigm: state and behavior traveling around together. Specifically, having objects with properties and methods. You might think that we’d lose this powerful abstraction if we stop using this. How can methods reference other bits of their own object if they can’t use this? Perhaps you’ve already guessed the answer: closures.

If you think about it, closure is another way of allowing state and behavior to travel around together. Let’s demonstrate by replacing some traditional this-based JavaScript with some closure-based JavaScript. Here’s a Car type implemented in vanilla JavaScript:

Here’s how we achieve the same behavior with closures:

I’ve implemented a createCar constructor function. It defines all the state and behavior of my Car type and returns an object that only exposes the public behavior of that type. Everything else defined in the constructor function is inaccessible to the outside world, but because of closure, everything defined within that constructor function can continue to access each other. Each call to the constructor function creates a new closure, a new little bag of collaborating state and behavior.

Inheritance

What about inheritance? Usually that’d be handled with prototypical inheritance, which would mean using this. That’s not allowed, so let’s get creative and achieve something like inheritance another way:

In this contrived example I’ve created a new MiniVan type which inherits all public functionality from Car and then adds some new functionality around capacity reporting. This is quite similar to the Mixins concept used in other languages like CLOS and Ruby. A potential drawback of this approach is that it doesn’t allow sub- or super-types to access internal state or behavior — in other words there’s no concept of “protected” visibility. However, I have only rarely seen a case where this protected access is useful. I would argue that in almost every case you can achieve the same ends in a better fashion using composition rather than inheritance.

Composition

Here’s how you might use composition to add new behavior to a type:

Inside my createCarWithOdometer constructor function I create an odometer, and then use that odometer‘s functionality to implement some extra methods. I then create a base car instance and mix the new behavior into that instance. What I have in the end is a new type which extends the Car type using the functionality provided by the odometer. All achieved without using prototypical inheritance, or this.

Really?

Yes, really. I worked on a team which built a quite large JavaScript application this way. We ended up using this maybe 10 times in the entire multi-thousand-line codebase, and we were quite happy about it.

Why did we find this approach successful? Firstly, it allowed us to sidestep the subtle gotchas involved with the way this works in JavaScript. No more confusion over jQuery re-binding this to the thing being iterated over, for example. Secondly, the ability to compose our own types on an ad hoc basis inside constructor functions turned out to be very powerful. It gives you the useful parts of multiple inheritance, but without the related diamond dependency issues. Finally, the biggest win was the fine-grained control of the API of each type, with all non-public functionality safely hidden from view inside the constructor function’s closure. I believe this was a significant reason for our success in working as a team on a large codebase.

tags: , ,

Get the O’Reilly Web Platform Newsletter

Stay informed. Receive weekly insight from industry insiders—plus exclusive content and offers.