Thursday, 26 January 2012

Object-oriented programming in Javascript without classes

Like a lot of people, I've been programming with objects a long time. Far too long in Java, and before that in Smalltalk, C++, and even CLOS. In all those languages, objects start with classes. Javascript's different though. There are actually two object-oriented programming models you can adopt: one's a traditional class-based language, whereas the other uses closures instead and lets Javascript's functional nature shine through.

Javascript Classes

The first of these programming models looks a lot like other object-oriented programming languages. Define a class, add a few methods, new up a few instances and poke them;

So far, so good. Let's not worry about whether this is a good way to deal with money or even whether it constitutes good object-oriented design. Instead, let's look at what this tells us about Javascript the object-oriented language. There are some things that might look like problems if you're used to "proper" (Java) classes. All the variables are public, come to that so are all the methods. There's also a certain amount of clutter - what's with the mysterious "prototype"? And there's some weird stuff going on with "this". Try this method:

This won't work as it stands. The keyword "this" refers to the instance of Account that we're working with inside a method of Account, but NOT inside a nested function. So in this example, "this" refers to the anonymous function that's mapped across the array. To get this to work I need this magic incantation:

To put it another way, this this isn't the same as that this. We Node JS programmers live in a world where every other line involves a nested function for NodeJS to call back, so we see a lot of this.

And now imagine that I want to make handleDebit private so that only handleSeveralDebits is public. There's no way to achieve this while handleDebit is still attached to the prototype. There are lots more hoops you can jump through (Douglas Crockford has several) but these just add to the clutter.

Javascript objects without classes

To recap: Javascript classes gave us public instance variables, the prototype special variable, "self=this", no private methods... Fortunately, Javascript is a functional language with closures as well. How about this implementation:

A simple function that returns an object. There's no class here, but that's ok, because in Javascript an object is whatever I say it is. Objects made like this will behave just like instances of the Account class, except that we won't be able to get at their instance variables or the private handleDebit method. We aren't using the "new" keyword, nor the "prototype", and best of all, not even the dreaded "this". There's simply less stuff to read. And there's less stuff to comprehend: just try googling "understanding the keywords prototype and constructor in JavaScript".

An alternative that some like is this:

Much the same, but the public api is a bit easier to scan.

Caveat emptor

It's not all good news though. These styles uses a lot more memory, because each instance has its own copy of each function. In Node 0.5.9, at least, this is the difference between (very roughly) 40 and 275 bytes per Account object. Which may or may not matter to your real application.

Conclusion

Javascript doesn't need classes. It's instructive that classes need some reserved keywords whose behaviour is easy to misunderstand, whereas the classless closure only needs the basic syntax of the language. I think this hints that programming with classes is an uneasy bolt-on intended to make the language approachable for Java exiles, whereas the pure core Javascript wants us to use classless objects with a pure functional syntax.

In this article we haven't talked about inheritance. My general attitude is that inheritance is a not particularly important special case of delegation, but that sounds like a topic for another day.

No comments: