First the path to the require module is resolved and the file is loaded.
After the module is loaded, the modules code is wrapped into a special function which will give us access to a couple of special objects.
The NodeJS runtime takes the code of our module and puts it inside an IIFE
(the wrapper function is an IIFE), so it means Node does not directly execute the code that we write in the module and that we required using the require
function, but instead its the “ wrapper (IIFE)
function” that will contain our code in it’s body and will then execute our code . The “wrapper IIFE” is also the one that passes the “exports
”, “require
”, “module
”, “__dirname
” “__filename
” into the module (file) and that is why in every module we automatically have access to stuff like the require
function.
→ By doing this Node achieve 2 important things :
i. Firstly doing this gives developer access to all the variables like “require”, “__filename”, etc.
ii. Secondly it keeps the “top-level variables ( variables declared outside of any function )” that we define in our modules private, so scoped only to the current module, instead of leaking everything to the global object.
Example : If we have 2 modules and we export module 1 to module 2 then all the top-level variables of module 1 are now scoped inside the wrapper function of the module 2 instead of leaking the everything from the module 1 into the global object. It promotes modularity, prevents naming conflicts, and offers controlled access to functionalities within modules
math.js (Module 1):
// Top-level variable (private)
const PI = 3.14159; function add(a, b) { return a + b;
} exports.add = add; // Expose the add function
app.js (Module 2):
const math = require('./math'); console.log(math.add(5, 3)); // Output: 8 (using the exported function) // Cannot access PI directly from math module
// console.log(PI); // This would result in an error
3.After that the code in the module’s “wrapper function” is EXECUTED by the Node.JS runtime.
4.Now after the code execution it time for the “require” function to return something and whats returns is the exports of the required module. This exports are stored in the “module.exports” object
→ When to use “module.exports
” or simply “exports
”
i. We use module.exports
to export single variable, e.g. one class or one function and set it equal to the variable (module.exports = Calculator
)
ii. We use exports
when we want to export multiple named exports like multiple function, for example , exports.add = (a+b) => a+b
5.And finally the entire module gets “Cached” after the first time they are loaded, meaning if we require the same module multiple times we always get the same result and the code and modules is only executed in the first call , in subsequent calls the result is then retrieved from the cache.