Modules & Bundling
CommonJS — require & module.exports
Master CommonJS module patterns in JavaScript. Learn module.exports, require syntax, caching behaviors, and synchronous module resolution.
1. Introduction
CommonJS (CJS) is a module format standardized for server-side environments (specifically Node.js). It manages modules using the module.exports object to share code and the require() function to import other files.
2. Why It Matters
CommonJS is the foundation of the Node.js ecosystem. Understanding its synchronous loading mechanics and caching behaviors is key to maintaining Node.js backends and legacy frontend projects that compile using CommonJS.
3. Real-World Analogy
Think of a Warehouse Tool Rental Desk:
- Synchronous loading (Waiting at the counter): You ask the clerk for a drill (require). The clerk walks to the back room, fetches the drill, and hands it to you. While the clerk is fetching the drill, you stand at the counter waiting, and no other tasks can proceed.
- Module Export (Placing items in the outgoing bin): When writing code, you place tools inside a designated rental bin (module.exports). Only the tools placed in the bin are accessible to other developers; everything else remains inside your private workspace.
4. CommonJS Syntax
In CommonJS, each file is treated as a private module. You share code by assigning it to the module.exports object:
5. Caching and Evaluation
CommonJS modules are evaluated synchronously and their exports are cached in memory:
• Synchronous loading: The code stops executing while the required file is read from the disk and parsed.
• Cached Exports: The first time a module is required, its code runs, and the exported object is cached. Subsequent require() calls return the cached object directly instead of running the module code again.
6. Practical Example
This script demonstrates exporting a singleton class instance using CommonJS:
7. Common Mistakes
- Overwriting module.exports after assigning properties to exports: Using both
exports.prop = valueandmodule.exports = newObjectcan cause confusion.exportsis simply a reference that points tomodule.exports. Overwritingmodule.exportscompletely breaks this reference, causing the properties assigned toexportsto be ignored.
8. Quick Quiz
Q1: How does require() behave when a module is imported multiple times in the same application?
A) The module is evaluated again for every call
B) The module is evaluated once; subsequent calls return the cached export object
Answer: B — CommonJS caches exported objects in memory. Subsequent require() calls return the cached object directly.
9. Scenario-Based Challenge
The Dynamic Config Switcher:
In a Node.js server, you want to load a configuration module dynamically based on the current environment variable: process.env.NODE_ENV. If the environment is "production", load ./config.prod.js, otherwise load ./config.dev.js. Write this loader logic using require().
10. Debugging Exercise
Explain why this import statement returns an empty object, and how to fix it:
// helper.js
exports = function calculate(a) {
return a * 2;
};
// app.js
const calc = require('./helper');
console.log(calc(5)); // crashes with TypeError: calc is not a function! Why?
View Solution
Diagnosis: The shortcut variable exports is simply a reference that points to the module.exports object. Reassigning exports = function... overwrites the shortcut variable reference, leaving module.exports as an empty object. The importing file receives the empty object instead of the function.
Fix: Assign the function to module.exports directly to overwrite the exported value correctly:
// helper.js
module.exports = function calculate(a) {
return a * 2;
};
11. Interview Questions
🟢 Q1: Explain how CommonJS modules are loaded and cached in Node.js.
Answer:
• Synchronous Loading: require() loads modules synchronously. The thread blocks while the file is read from the disk and parsed, which is fine for server-side environments but inefficient for client-side applications.
• Caching: When a module is loaded, its code runs, and the exported object is cached. Subsequent require() calls return the cached object directly instead of running the code again. This ensures that the module acts as a singleton.
12. Production Considerations
- • Transition to ESM: While CommonJS remains widely used in Node.js, modern projects are moving toward ES Modules (ESM) because it supports static analysis and tree shaking. For new projects, use ESM (using
import/export) instead.