I have been going through some online tutorials on Node.js. What I understood is that, on using require(./file-path)
function, the node gets the content of that file and wraps inside an immediately invoking function
(function(exports, require, module, __filename, __dirname) {
// content
}())
I understood the difference between exports
and module.exports
. That's all I can see in the internet on searching the above question. But what my question is , why we need to pass module.exports
and module
to the wrapping IIFE? We could have passed module alone and then get module.exports
from it. Is there any advantage on doing like this? Normally when we pass an object to a function , we don't have to pass object.property
additionally.
The answer is: historical reasons.
You're right, we could have only module
and exports
would not be needed but it's still there for backwards compatibility.
It used to be a time when the module wrapper was changed in pretty much every patch release.
In Node 0.1.11 the module wrapper was:
var wrapper = "function (__filename) { "+
" var onLoad; "+
" var onExit; "+
" var exports = this; "+
content+
"\n"+
" this.__onLoad = onLoad;\n"+
" this.__onExit = onExit;\n"+
"};\n";
See: https://github.com/nodejs/node/blob/v0.1.11/src/node.js#L167#L177
As you can see the exports
was the same as the this
that the wrapper function was called with. You couldn't swap it with a new object and you couldn't even add some reserved keys to it - e.g. you couldn't safely export a property named __onExit
.
Then in 0.1.12 it was:
var wrapper = "function (__filename, exports) { " + content + "\n};";
See: https://github.com/nodejs/node/blob/v0.1.12/src/node.js#L243-L245
Here the exports
was an object supplied as one of the arguments but you couldn't swap it with a new object, you could only add or remove properties from the object that you got.
Then the 0.1.13 was the first to have this, i.e. require
and include
:
var wrapper = "function (__filename, exports, require, include) { " + content + "\n};";
See: https://github.com/nodejs/node/blob/v0.1.13/src/node.js#L225-L227
Then 0.1.14 was the first to have __module
(with underscores) in the wrapper (and that dropped include
):
var wrapper = "var __wrap__ = function (__module, __filename, exports, require) { "
+ content
+ "\n}; __wrap__;";
See: https://github.com/nodejs/node/blob/v0.1.14/src/node.js#L280-L284
And the 0.1.16 was the first to have a module
argument (with no underscores) in the wrapper:
var wrapper = "var __wrap__ = function (exports, require, module, __filename) { "
+ content
+ "\n}; __wrap__;";
See: https://github.com/nodejs/node/blob/v0.1.16/src/node.js#L444-L448
It's been changed many times after that but this is the time that the module
got introduced making the exports
not necessary any more but still a useful shortcut, allowing you to use:
exports.a = 1;
exports.b = 2;
exports.c = 3;
instead of:
module.exports.a = 1;
module.exports.b = 2;
module.exports.c = 3;
though in practice if there was no exports
then one would usually write:
const exports = module.exports;
exports.a = 1;
exports.b = 2;
exports.c = 3;
or more likely:
module.exports = {
a: 1,
b: 2,
c: 3,
};
or, to have some checks in static analysis tools:
const a = 1;
const b = 2;
const c = 3;
module.exports = { a, b, c };
There are many ways to do it, it's a pretty flexible mechanism.