Saturday, March 1, 2014

Node.js - modules and exports

I think most of us haven't understood the concept of modules in Node.js properly. Let us discuss the basics of that in this post.

Module System

In Node.js, when you create a new JavaScript file, that will be considered as a separate module. Inside that module, you can access the module itself with module object. You can check that, like this

console.log(module);
which produces something like this
{ id: '.',
  exports: {},
  parent: null,
  filename: '/home/thefourtheye/Desktop/Test.js',
  loaded: false,
  children: [],
  paths:
   [ '/home/thefourtheye/Desktop/node_modules',
     '/home/thefourtheye/node_modules',
     '/home/node_modules',
     '/node_modules' ] }

exports and module.exports

As you can see, it is just a plain JavaScript object. The important thing to be noted here is, the exports object in module. In every module, JavaScript, by default, offers another variable called exports. That is nothing but the same object in module object of the same name. You can check that like this

exports.jabberwocky = "blah blah blah";
console.log(module.exports);            // { jabberwocky: 'blah blah blah' }

So, they are one and the same. But, when some other module requires this module, the object returned will be module.exports only. As long as you are augmenting module.exports and exports, there will be no problem. But when you assign something to either exports or module.exports, they no longer refer to the same object.

exports = {"king": "Sourav Ganguly"};
console.log(module.exports);           // {}
console.log(exports);                  // { king: 'Sourav Ganguly' }

You are making both exports and module.exports refer to different objects. So, when this module is exported, an empty object will be exported (remember, only module.exports will be exported when required from other files), even though we assigned a valid object to exports. So, care should be taken when you replace either of those objects. That is the reason why we often see something like this

exports = module.exports = ...

Scope

All the variables and functions declared within the module will be accessible only inside the module (as long as they are created with var keyword). Quoting from the modules documentation

Variables local to the module will be private, as though the module was wrapped in a function.
Happy modularizing the code :)