Master Node.js Module System: Loading, Caching, and CommonJS Pitfalls
This article explains Node.js's CommonJS module system, covering how require loads modules, the role of module.exports versus exports, module classification, loading order, caching behavior, circular dependencies, and provides practical code examples for interview preparation.
Interview Guide
requireloading mechanism? (see module loading mechanism)
Difference between module.exports and exports (object reference relationship)
What happens when a.js and b.js reference each other? (circular dependency problem 1)
Will the undeclared variable in module a be printed in b.js? (circular dependency problem 2)
Is the require process synchronous or asynchronous? (see file module loading)
Module Classification
System Modules
C/C++ built‑in modules, used for native calls.
Native modules such as http, buffer, fs that internally rely on built‑in C/C++ modules.
Third‑Party Modules
Modules not bundled with Node.js. They include path‑based file modules (starting with ., .., or /) and custom modules like express, koa, moment.js.
JavaScript module example: hello.js JSON module example: hello.json C/C++ compiled module example:
hello.nodeDirectory Structure
├── benchmark // Node.js performance test code
├── deps // Node.js dependencies
├── doc // Documentation
├── lib // Exposed JS modules
├── src // C/C++ source files (built‑in modules)
├── test // Unit tests
├── tools // Build tools
├── vcbuild.bat // Windows makefile
├── node.gyp // node‑gyp configuration
...Module Loading Mechanism
When a module is required, Node.js typically follows three steps: path resolution, file locating, and compiling/executing.
Loading order based on module type:
System cache : If the module has been executed before, its cached export is returned.
System modules : Native modules are loaded directly after cache check, bypassing path resolution and file locating.
File modules : Modules whose paths start with ., .., or / are loaded next. If no extension is provided, Node.js tries .js, .json, then .node synchronously.
Directory as module : If a directory is found, Node looks for a package.json and uses its main field; otherwise it throws Cannot find module.
node_modules lookup : If the module is still not found, Node searches parent directories up to the filesystem root.
Module Cache Location
After a module is loaded, its exports are stored in require.cache. The following example demonstrates how to inspect the cache.
test-module.js
module.exports = {
a: 1,
test: () => {}
};test.js
require('./test-module.js');
console.log(require.cache);The output shows each cached module’s filename, path, and exported data.
Module Circular References
Problem 1
What happens when a.js and b.js require each other? Does it cause an infinite loop?
// a.js
console.log('a module start');
exports.test = 1;
undeclaredVariable = 'a module undeclared';
const b = require('./b');
console.log('a module finished: b.test =', b.test); // b.js
console.log('b module start');
exports.test = 2;
const a = require('./a');
console.log('undeclaredVariable:', undeclaredVariable);
console.log('b module finished: a.test =', a.test);Running node a.js loads b.js while a.js is still executing. Node provides b with an unfinished copy of a 's exports, preventing a true dead‑loop. After b finishes, its completed exports are assigned to a.
Problem 2
Will the undeclared variable from a be printed in b.js ?
Because undeclaredVariable becomes a global variable, it is accessible from other modules.
Object Reference Relationship
Most interviewers ask: what is the difference between module.exports and exports ?
exportsis a shortcut to module.exports: const exports = module.exports; However, reassigning exports changes its reference and breaks the link. The correct way is to add properties to exports or assign directly to module.exports:
// Correct
module.exports = { a: 1, b: 2 };
// Incorrect – will result in <code>undefined</code>
exports = { a: 1, b: 2 };Understanding this relationship helps avoid common pitfalls when exporting from Node.js modules.
Node Underground
No language is immortal—Node.js isn’t either—but thoughtful reflection is priceless. This underground community for Node.js enthusiasts was started by Taobao’s Front‑End Team (FED) to share our original insights and viewpoints from working with Node.js. Follow us. BTW, we’re hiring.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
