Understanding IIFE, Module Privacy, and require
in Node.js
Overview
Immediately Invoked Function Expressions (IIFE) in Node.js, how to keep module variables and functions private, and details the mechanism of the require
statement in Node.js.
Immediately Invoked Function Expression (IIFE)
An IIFE is a JavaScript design pattern that runs a function immediately after it's defined. It helps create a private scope and avoids polluting the global namespace.
Syntax
(function() {
// Code inside here is private
})();
With Parameters
(function(param) {
console.log(param);
})(5); // Logs 5
Use in Node.js
IIFEs are useful in Node.js for:
- Encapsulation: Keep variables and logic private within a module.
- Avoiding Globals: Prevent variable leakage to the global scope.
- Self-contained Blocks: Useful within larger modules or libraries.
Example
// myModule.js
const myModule = (function() {
const privateVariable = 'I am private';
function privateFunction() {
console.log('This is a private function');
}
return {
publicFunction: function() {
console.log('This is a public function');
privateFunction();
}
};
})();
module.exports = myModule;
Module Privacy in Node.js
Each module in Node.js has its own scope. Variables/functions inside a module are private unless explicitly exported.
Example
// myModule.js
const privateVariable = 'I am private';
function privateFunction() {
console.log('This is a private function');
}
module.exports = {
publicFunction: function() {
console.log('This is a public function');
}
};
require
Mechanism in Node.js
The 5 Steps of require()
- Resolving – Determines full file path
- Loading – Reads file contents
- Wrapping – Wraps code in function:
(function (exports, require, module, __filename, __dirname) {
// Module code
});
- Evaluation – Executes the wrapped code
- Caching – Stores the result for future calls
Example
// index.js
const myModule = require('./myModule');
myModule.publicFunction();
Module Wrapping in Node.js
All code in Node.js modules is wrapped in an IIFE. This provides local scope.
What You Write:
const x = 100;
module.exports = () => 'Hello';
What Node.js Actually Executes:
(function(exports, require, module, __filename, __dirname) {
const x = 100;
module.exports = () => 'Hello';
})(module.exports, require, module, __filename, __dirname);
Module Caching
Node.js caches modules after first require
. Future requires return cached version.
const mod1 = require('./module.js');
const mod2 = require('./module.js');
// mod1 === mod2 (same object from cache)
Exploring Node.js Source Code
- CommonJS Loader:
lib/internal/modules/cjs/loader.js
- ESM Loader:
lib/internal/modules/esm/loader.js
- Helpers:
lib/internal/modules/helpers.js
Key Internal Functions
Module._load()
– Main loading logicModule._compile()
– Compiles/wraps module codeModule.wrap()
– Creates the IIFE wrapper
Practical Examples
Accessing Module Variables
// module.js
const privateVar = 'secret';
module.exports = { publicVar: 'hello' };
// app.js
const mod = require('./module.js');
console.log(mod.publicVar); // 'hello'
console.log(mod.privateVar); // undefined
Using Module Parameters
// module.js
console.log(__filename); // Full file path
console.log(__dirname); // Directory path
Behind the Scenes Example
index.js
const num = 10;
let msg = 'I am Index.js';
module.exports = { num, msg };
app.js (imagined IIFE)
(function(exports, require, module, __filename, __dirname){
console.log('App file loading');
const { num, msg } = require('./index.js');
console.log('num:', num, 'msg:', msg);
console.log('app end');
})(...);
Types of require()
calls
- Internal file:
require('./filename.js')
- JSON:
require('./data.json')
- Text/log file:
require('./logs.txt')
- Core module:
require('http')
- External npm module:
require('express')
How module.exports
Exposes Private Modules
When you use module.exports
, you decide what gets exposed to the outside world. Even private variables or functions can be made public if you include them in the exported object.
Example: Private to Public Exposure
// user.js
const secret = 'top-secret';
const getSecret = () => secret;
module.exports = {
reveal: getSecret // Exposing private function
};
// app.js
const user = require('./user');
console.log(user.reveal()); // Outputs: 'top-secret'
Here, secret
stays private, but getSecret()
is shared via module.exports
.
Complete require()
Flow with Types
When you call require()
, Node.js performs the following steps:
-
Resolving: Determine the absolute file path.
- Local file:
require('./file.js')
- JSON file:
require('./data.json')
- Text/Log file:
require('./file.txt')
- Core module:
require('fs')
,require('http')
- Installed npm module:
require('express')
- Local file:
-
Loading: Node reads the file contents.
-
Wrapping: Code is wrapped in:
(function(exports, require, module, __filename, __dirname) { ... })
-
Evaluation: Executes the code.
-
Caching: Saves the output for reuse.