Call me old, boring, whatever, but I like patterns. When one has solved the same problem a millions times over why reinvent the wheel? So as I've been working with NodeJS, I've been thinking about how to perform some patterns in Javascript. I've been getting into Typescript a bit too for Domain Driven Design as I find working with a classical OOP paradigm is cleaner for modelling ones domain, and Typescript has some nice syntatic sugar.
As part of my adventures into Javascript I've read Crockford's Javascript: The Good Parts, I'm in NodeJS In Action and I'm finding the blog over at HowToNode helpful with their practical "how tos".
One big area of confusion for me is the use of the new keyword when doing OOP in Javascript.
Crockford writes: "Functions that are intended to be used with the new prefix are called constructors. By convention, they are kept in variables with a capitalized name. If a constructor is called without the new prefix, very bad things can happen without a compile-time or runtime warning, so the capitalization convention is really important. Use of this style of constructor functions is not recommended. We will see better alternatives in the next chapter." He then writes about Prototypal Inheritance. A good explanation of the same concept, and how it relates to NodeJS is over at the HowToNode site by Tim Caswell.
Caswell echos Crockford's concerns: "I don't like the new keyword, it overloads the meaning of functions and is dangerous. If we were to say frank = Person("Frank"), then this inside the function would now be the global this object, not the new instance! The constructor would be overriding all sorts of global variables inadvertently. Also bad things happen if you return from a constructor function."
So one might think that new is a bad idea, and we should all use less dangerous (and possibly less confusing) syntax. Most of the alternatives use property copying, as seen in either Crockford's examples or Caswell's.
Then I read the following code snippet in "NodeJS In Action"
var events = require('events'); var channel = new events.EventEmitter();
If one also looks at the output of the Typescript Playground for "simple inheritance" we see the usage of new keyword on the JS side (I can understand it's usage on the TS side as TS is mimicking classical inheritance).
Looking more into what the new keyword actually does we find it's actually doing is setting some properties and setting up the prototype chain.
So is the new keyword that bad, and should we be using it? I seem to be getting mixed messages, and I personally don't see the problem with it. If you're TDDing your code then you're going to pick up bugs related to forgetting to use the keyword. The alternatives seem to duplicate the behaviour of the keyword implementation, and you have to include the source for your particular alternative (for example Crockford and Caswell provide similar alteratives; which one do you include in your app?). NodeJS makes this a bit easier with it's Utils.inherits method.
Does the new keyword make the code harder to understand because it obscures Javascripts prototypical nature and lures unsuspecting readers into troubles and snares? Crockford does write "JavaScript is conflicted about its prototypal nature. Its prototype mechanism is obscured by some complicated syntactic business that looks vaguely classical. Instead of having objects inherit directly from other objects, an unnecessary level of indirection is inserted such that objects are produced by constructor functions." Do the alternative property copying functions read better, and help make the intent of the program clearer? If you look at the Typescript Playground's __extends method I think not.
To new or not to new? Currently I'm leaning towards using the new keyword. I'm going to have to think on this some more, to develop a pattern for my own apps, but I'm open to suggestions/comments.
Great article. We are doing some node work at the moment too, for very similar reasons to you. I think that JavaScript's object model is crappy, but it's all we've got so you should use new. Coffee script provides a more robust class model, but it's not for everyone.
ReplyDeleteThanks @Bruce for your comments.
ReplyDeleteCoffeeScript's object model is very similar to Typescript (right down to the examples/generated code on the respective homepages; although CS did come first) where they both use new. I personally prefer TS over CS as I prefer braces to delimit my blocks over whitespace (cue the CS/Python lovers). I also feel that CS is trying to hide the underlying JS when one should just embrace writing good JS to begin with :p
+1 towards sticking with new then ;)