fiveisprime the more you node...

exports or module.exports?

Node modules are sandboxed and exposed using either module.exports or exports. If you have written any kind of module then you have used this mechanism before, but what is the difference between exports and module.exports and which should you use?

tl;dr

Use exports to attach members; module.exports to override the return type.

Figuring out the difference

Let's start with two files in the same directory: exports.js and app.js.

exports.js

exports = function() { console.log('Hey there'); }

app.js

var test   = require('./exports')
  , assert = require('assert')

assert.strictEqual(
  typeof test
, 'function'
, 'should return a function');

Run the above using node app.js and you will see that the following error is thrown:

AssertionError: should return a function

This happens because changing what exports is (changing the pointer) actually disconnects the exports variable from the Node.js environment. Updating app.js makes this a little clearer:

app.js

var test = require('./exports')
  , util = require('util');

console.log(typeof test);
console.log(util.inspect(test));

Which outputs the following when run:

object
{}

What's actually being returned by our module is module.exports - this is why the return type is object rather than undefined. Let's update exports.js.

module.exports = function() { console.log('Hey there'); }

Run app.js again which will change the output to something that makes more sense:

function
[Function]

What's going on here?

Fortunately for all of us, the source for Node.js is available so that you can figure this part out. In /src/node.js you can see that your code is wrapped in a closure and passed both exports and module. Of course, further inspection will show you that exports contains a pointer to module.exports and suddenly everything makes sense.

Overwriting exports overwrites the pointer to module.exports which disconnects exports from the Node.js environment!

What's the point?

Exports is a helper function that points to module.exports. This is meant to make your life easier. That is all. Use it to expose functions of your module, but if your module needs to replace what is exposed, you must use module.exports.

Different Ways to Declare Functions

There are a few different ways to declare a function in JavaScript (function definitions, function expressions, and methods) and each has its own set of strengths and weaknesses. This is a simple overview of the differences between function definitions and expressions.

Function definition

Function definition is most like other languages. I came from a background in C# so this made a lot of sense to me starting out with JavaScript.

function foo() {
    // Do things.
    foo();
}
  • Not anonymous
  • Shows up named in the stack trace
  • Not hoisted
  • Allows recursive calls and is available outside of function scope

Function expression

Function expressions show off a strength of JavaScript - assigning functions to variables. Again, as a C# developer, this was a bit awkward at first, but is now one of my preferred methods for declare a function (unless recursion is necessary).

var bar = function() {
    // Do things.
}
  • Is technically anonymous
  • Shows up in stack trace as (Anonymous function)
  • Hoisted
  • Unable to call itself recursively but available outside of function scope

Named function expression

This is a mixture of the two. Function declarations are named which means that they show up in a stack trace as the name of the function rather than Anonymous. Using a named function expression lets you use the function as a variable and you get the benefit of a function declaration.

var baz = function baz() {
    // Do things.
    baz();
}
  • Not anonymous
  • Shows up named in the stack trace
  • Hoisted
  • Able to call itself recursively

Function expressions may be used conditionally:

var foo
  , handler = App.createHandler();

if (response) {
    foo = handler.foo;
} else {
    foo = function() {
        // Some default functionality...
    }
}

foo();