Inheritance and Initialization in Node.js
Javascript inheritance is an interesting topic, rife with pitfalls for the unwary, and this is no less true in node.js than in any other modern application of Javascript. For example, this is a common gotcha that I noted while translating thywill-python, an experiment-with-slash-exploration-of Comet frameworks, into node.js. Let's say I have a hierarchy of classes, A, B, and C, which extend out from EventEmitter as follows:
var EventEmitter = require("events").EventEmitter; var util = require("util"); function A() { this.className = "classA"; } util.inherits(A, EventEmitter); function B() { this.className = "classB"; } util.inherits(B, A); function C() { this.className = "classC"; } util.inherits(C, B);
The function util.inherits(subclass, superclass) is a shortcut for one of the methods of Javascript inheritance, provided in core node.js, and it looks like this under the hood:
var inherits = function (ctor, superCtor) { ctor.super_ = superCtor; ctor.prototype = Object.create(superCtor.prototype, { constructor: { value: ctor, enumerable: false } }); };
Note that you'd have to be using Chrome or Firefox 4 if you want that to work on a web page - Object.create is a comparatively recent addition to Javascript.
In this example, my particular need is for all instances of subclasses of B (i.e. instances of C in this case) to notify a central repository of their existence and then emit an event marking their creation. A logical place to put this code is B's constructor and prototype definition:
var registry = require("./centralRegistry"); function B() { this.className = "classB"; this._register(); } util.inherits(B, A); B.prototype._register = function() { // Set the variable "self" to avoid issues with scope and "this" in the callback var self = this; // Register this instance and pass a callback to emit an event once registered. registry.register(this, function() { self.emit("created and registered", self }); }
The gotcha for folk coming from languages or frameworks with automatic constructor chaining is that, as written, this only works when you are instantiating class B. The util.inherits() function doesn't chain constructors, as some people might expect it to after a brief glance at the documentation. If you instantiate a subclass like C then the _register() method is not invoked - because the invocation is in B's constructor, which is also not invoked.
To make this work you need to explicitly call the constructor of a superclass in the constructor for an inherited class, and you'll note that util.inherits() helpfully sets a reference to the superclass for you in the form of "super_":
var registry = require("./centralRegistry"); var EventEmitter = require("events").EventEmitter; var util = require("util"); function A() { A.super_.call(this); // not necessary, but it's neater to keep it there. this.className = "classA"; } util.inherits(A, EventEmitter); function B() { B.super_.call(this); this.className = "classB"; this._register(); } util.inherits(B, A); B.prototype._register = function() { // Set the variable "self" to avoid issues with scope and "this" in the callback var self = this; // Register this instance and pass a callback to emit an event once registered. registry.register(this, function() { self.emit("created and registered", self }); } function C() { C.super_.call(this); }; util.inherits(C, B);
The call() method is a property of all functions, much like apply(). Using function.call(object) invokes the function and sets the value of "this" inside the function to be the passed object. Further parameters provided to call() are passed through to the function, which here isn't important. So in the code above, the constructors use call() to invoke superclass constructor functions, and ensure that "this" in those superclass constructors is set to be the newly instantiated object.