TL;DR - Code in NodeJS modules can continue to access the non-exported things they define within the modules because they and the functions they define close over that data.
Details:
NodeJS modules are effectively big functions that get called by NodeJS when you require
them. (They're only called once, regardless of how many times they're require
d.) A function call in JavaScript sets up an execution context and any functions created within that context close over the context (they have an enduring reference to it and its contents). The execution context survives in memory as long as anything has a reference to it, even after the "function" has returned. The functions created within the execution context are "closures".
So say you have a module foo
:
var privateData = Math.random();
function publicFunction() {
console.log("The private data is " + privateData);
}
module.exports.publicFunction = publicFunction;
and you require
it:
var foo = require("foo");
foo.publicFunction(); // Displays the random number
That's analogous to having a function that returns an object:
function fooModule() {
var privateData = Math.random();
function publicFunction() {
console.log("The private data is " + privateData);
}
return {
publicFunction: publicFunction
};
}
that you call:
var foo = fooModule();
foo.publicFunction(); // Displays the random number
The same mechanism is at work in both places.
More to explore: