Monday, January 7, 2013

Exporting a JavaScript "object" in node

It's simple to use module.exports if you want to distribute a few static functions.  It becomes an entirely different matter if you want to take some sort of input in a constructor.  In this case, you'd want to take advantage of JavaScript's Prototypical Object-Oriented system.  This isn't a tutorial on POO (heh), as MDN already has a good one.

Rather, we're going to tackle the node side of things.  Let's say we've got an incredibly simple system (in foo.js):
'use strict';
function Foo(bar) {
  this.baz = bar;
}
Foo.prototype = {
  foobar: function() {
    alert(this.baz);
  }
};
module.exports = Foo;
 If we then wanted to use it elsewhere, how would we require it?  Seeing other examples, we might be tempted to do something along the lines of:
var foo = require('./foo')('Kittens');
or
var foo = new require('./foo')('Kittens');
These will result in the rather odd error "TypeError: Cannot set property 'baz' of undefined." on the line "this.baz = bar;"  This is a side effect of using strict mode (which you should, for exactly this occasion) where the object is not being initialized properly.  While the above seem like they should work, there's one little kink that needs to be worked out.  While it's possible to get members of something required, like so:
var a = require('./b').a;
 This won't work for prototypical objects that depend on `this` like what we've got above.  Instead, you need to require, and then use new:
var fooProto = require('./foo')
  , foo = new fooProto('Kittens');
It's also possible to use a less clear shortcut:
var foo = new (require('./foo'))('Kittens');
While I am by no means an node expert, I hope this helps you.  Good luck!

1 comment:

  1. I'd like to offer up an alternative: Instead of using constructor functions (pseudo-classical style), use a more purely prototypal style. Set module.exports to an object, which can then be used as the prototype of new objects via Object.create(). For initialization, my preference is to include an "init" function on the prototype object, though a factory pattern or simply setting properties could be used as well.

    Check out https://github.com/ryankinal/node-content/blob/master/content/article.js for the prototype code, and https://github.com/ryankinal/node-content/blob/master/content-db/content-db.js for the "instantiation" code.

    ReplyDelete