Your intuition is correct. I'll work from the bottom up:
The Node.js Wrapper
Before running any file, Node.js wraps the entire script in an immediately-invoked function expression (IIFE):
(function (exports, require, module, __filename, __dirname) {
// ...script goes here...
});
This is how it introduces the module
and exports
variables into the scope; they're nothing more special than function arguments.
Using exports
The Node.js docs on module.exports
and the exports
alias are pretty helpful. To start out, module.exports
and the exports
variable both refer to the same empty object that was created by the module system.
If a module simply needs to export a plain old JavaScript object with some properties set, exports
is all that's needed:
exports.get = function(key) {
// ...
};
exports.set = function(key, value) {
// ...
};
When code require()
s this module, the result will look something like:
{
get: [Function],
set: [Function]
}
Replacing module.exports
However, Express exports a constructor function, createApplication
, as the root value. To export the function itself, rather than just assigning it to a property on the exported object, it must first replace the exported object entirely:
module.exports = createApplication;
Now, exports
hasn't been updated and still refers to the old object, whereas module.exports
is a reference to createApplication
. But if you keep reading, you'll notice that Express exports several other properties in addition to the constructor. While it would be sufficient to assign these to properties on the new value of module.exports
, it is shorter to reassign exports
so that it is again an alias to module.exports
, then assign those properties on exports
, which is equivalent to assigning them on module.exports
.
Thus, these examples are functionally equivalent:
module.exports = createApplication;
function createApplication() {
// ...
}
module.exports.application = proto;
module.exports.request = req;
module.exports.response = res;
But this one is less verbose:
exports = module.exports = createApplication;
function createApplication() {
// ...
}
exports.application = proto;
exports.request = req;
exports.response = res;
Either way, the result is the same, a function named createApplication
at the root with properties available on it:
{
[Function: createApplication]
application: [Object],
request: [Object],
response: [Object],
// ...a few other properties...
}