Readable Class Definitions in Node.js
I don't know about you, but I find long, brace-strewn Javascript constructors hard on the eyes - you've lost me long before adding a fifth multi-indent method to the return scope in your uberconstructor. My eye just skates off the page. If I add five multi-indent methods to the return scope of a constructor, then I'm certainly losing the instance of me who will come back in a few months to fix the bug I'm unwittingly introducing somewhere in all that nesting.
One of the pleasant things about server-side Javascript, and Node.js in particular, is that developers tend to revert to the habit of defining one class per file and spreading out their code across many files in a familiar and logical fashion. The Node.js pattern of cached requires and module.exports means that you can use these framework aspects almost as natural breaks in your code. It's a very different world from the client-side Javascript impetus to push as much as possible into a single file.
In any case, the following example shows how I divide up and lay out class definitions in Node.js for my own readability; the class is from my current slowly-progressing side project, thywill.js. Of particular note: I don't put method definitions inside the constructor, I declare the need for subclass methods by creating definitions that throw exceptions when invoked, and I assign Class.prototype to a convenience variable. You can only type "Class.prototype" so many times before being driven mad.
var util = require("util"); var Component = require("../component"); //----------------------------------------------------------- // Class Definition //----------------------------------------------------------- /* * The superclass for interfaces between thywill and applications built on * thywill.js. These interfaces manage the communication between applications * and thywill.js. */ function ApplicationInterface() { ApplicationInterface.super_.call(this); this.componentType = "applicationInterface"; this.clientInterface = null; }; util.inherits(ApplicationInterface, Component); var p = ApplicationInterface.prototype; //----------------------------------------------------------- // Initialization //----------------------------------------------------------- p.setClientInterface = function(clientInterface) { this.clientInterface = clientInterface; }; //----------------------------------------------------------- // Methods //----------------------------------------------------------- p.send = function(client, message) { this.clientInterface.send(client, message); }; //----------------------------------------------------------- // Methods to be implemented by subclasses. //----------------------------------------------------------- p.receive = function(client, message) { throw new Error("Not implemented."); }; p.connection = function(client) { throw new Error("Not implemented."); }; p.disconnection = function(client) { throw new Error("Not implemented."); }; //----------------------------------------------------------- // Exports - Class Constructor //----------------------------------------------------------- module.exports = ApplicationInterface;
What if I wanted the ApplicationInterface class to be instantiated as a singleton instance? Then I would set module.exports to be a newly instantiated ApplicationInterface object. The system of require() and module.exports in Node.js is an excellent methodology for maintaining singletons, in fact, since the various exports values are cached - a require() call to the same file in two different files will return the same object. So for a singleton ApplicationInterface, the end of the file would look like this:
//----------------------------------------------------------- // Exports - Singleton Instance //----------------------------------------------------------- module.exports = new ApplicationInterface();
I hope you'll agree that this is very much more readable than some of what you'll see while browsing Github, especially when methods become long and nested with callbacks.