Metaduck

Dump This And Prototype

(How I like to build classes in Javascript)

When developing server-side Javascript for Node.js, generally I tend to encapsulate classes inside CommonJS modules and expose the constructor function as module itself.

As an incomplete example of how I used to do it, let’s build a module that exposes a rectangle class:

function Rectangle(x, y, width, height) {
  this.x = x;
  this.y = y;
  this.width = width;
  this.height = height;
}

Rectangle.prototype.area = function() {
  return this.width * this.height;
};

module.exports = Rectangle;

Let’s say you save this module under the name “rectangle.js”, on the current directory.

Then, to instantiate a rectangle you must do:

var Rectangle = require('./rectangle');
var rectangle = new Rectangle(1,2,3,4);
rectangle.area(); // -> 12

All is fine and dandy, right?

Nope. This way you can tamper with the rectangle object, changing properties and even overriding functions. I think this is not a major problem, but exposes a major design flaw, which I’ll cover later.

Now you want to add a private function. You have two main options: 1) add it as a function on the module scope or 2) add it as a function on the Rectangle.prototype object, but giving it an underscore so everyone knows they shouldn’t be calling.

Lets’ say for the purpose of the example, that you want to add a provate function named “coalesce”, which you want to call after the constructor.

1) Add it to the module scope

function coalesce() {
  var self = this;
  ['x', 'y', 'width', 'height'].forEach(function(prop) {
    if (!self[prop]) { self[prop] = 0; }
  });
}

function Rectangle(x, y, width, height) {
  this.x = x;
  this.y = y;
  this.width = width;
  this.height = height;
  coalesce.apply(this);
}

Rectangle.prototype.area = function() {
  return this.width * this.height;
};

module.exports = Rectangle;

Here we can see the constructor calling the “coalesce” function using the function.apply(), which sets the “this” scope, which then the coalesce function can use as the object.

2) Add it as a function on the Rectangle.prototype

function Rectangle(x, y, width, height) {
  this.x = x;
  this.y = y;
  this.width = width;
  this.height = height;
  this._coalesce();
}

Rectangle.prototype.area = function() {
  return this.width * this.height;
};

Rectangle.prototype._coalesce = function() {
  var self = this;
  ['x', 'y', 'width', 'height'].forEach(function(prop) {
    if (!self[prop]) { self[prop] = 0; }
  });
};

module.exports = Rectangle;

This way is simpler, but we’re exposing the coalesce function, which is ugly.

The problem

As I said earlier, this pattern exposes the methods and the data on the rectangle object.

The ultimate goal would be to expose the methods and encapsulate the data. How can we do that?

Here is a solution I like to use:

function Rectangle(x, y, width, height) {

  function area() {
    return width * height;
  };

  function coalesce() {
    if (! x) { x = 0; }
    if (! y) { y = 0; }
    if (! width) { width = 0; }
    if (! height) { height = 0; }
  }

  coalesce();
  return {
    area: area
  };
}

module.exports = Rectangle;

And a client of this module would look like:

var Rectangle = require('./rectangle');
var rectangle = Rectangle(undefined, undefined, 3, 4);

What have we done here?

The constructor simply returns an object that has the methods we want to expose. The data is encapsulated inside the constructor function, which also contains all the functions (private and public) that have privileged access to these.

Then we’re dropping the using of “new” notation on the class clients (which could cause a lot of problems on the previous model if module clients omitted it).

This pattern also allows for object methods (private or public) to call each other with no restraints, since we are not relying on the leaky this object.

What about inheritance?

A useful way of declaring that a class (or pseudo-class, if you will) inherits from another one is having the constructor prototype pointing to an object that it “inherits” behavior from. Node (and almost all the Javascript frameworks) has convenience function for doing this in util.inherit().

For instance, say you want our Rectangle class (as in our first incarnation) inheriting from the Node EventEmitter class:

var inherit = require('util').inherit
  , EventEmitter = require('events').EventEmitter;

function Rectangle(x, y, width, height) {
  this.x = x;
  this.y = y;
  this.width = width;
  this.height = height;
}

inherit(Rectangle, EventEmitter);

Rectangle.prototype.area = function() {
  return this.width * this.height;
};

module.exports = Rectangle;

Convenient, heh?

(You must be careful to call inherit before setting the prototype properties, or else they will be nuked).

How can we then implement inheritance if we’re not using the tradicional Javascript constructor functions?

Here is a way:

var EventEmitter = require('events').EventEmitter;

function Rectangle(x, y, width, height) {
  var that;

  function area() {
    return width * height;
  };

  function coalesce() {
    if (! x) { x = 0; }
    if (! y) { y = 0; }
    if (! width) { width = 0; }
    if (! height) { height = 0; }
  }

  coalesce();

  that = {
    area: area
  };

  that.__proto__ = EventEmitter.prototype;

  return that;
}

module.exports = Rectangle;

So, we’re using the __proto__ object, which is reserved in Javascript for the actual prototype object. So if you call any EventEmitter-specific methods like on() and emit(), the runtime will look into the rectangle object, and if not found, will search inside the prototype chain.

Even though it is standard, bear in mind you that the __proto__ object is not entirely portable to all Javascript platforms and browsers, but there are ways around that.