JS: CommonJS (tutorial)
explain the common.js module system
explain the common.js module system
(ai answer)
CommonJS is a specification for JavaScript modules that was originally developed to standardize module loading in server-side environments, particularly for Node.js. It provides a way to organize code into reusable, encapsulated units (modules) that can depend on and export functionality to other modules. Unlike browser-based JavaScript (which historically lacked native module support), CommonJS treats each file as a separate module, enabling better code organization, dependency management, and avoidance of global namespace pollution.
CommonJS is synchronous by design, meaning modules are loaded and executed immediately when required, which makes it well-suited for server-side applications where startup time is less critical than in browsers. It's the foundation of Node.js's module system (via the require function) and has influenced tools like Webpack and Browserify for bundling client-side code.
Core Concepts
-
Modules as Files: Each JavaScript file (e.g.,
math.js) is treated as a self-contained module. There's no global scope shared between modules—each runs in its own isolated context. -
Exports: A module defines what it makes available to other modules via an
exportsobject (ormodule.exportsfor the entire module). -
Imports: Other modules load dependencies using
require(), which returns the exported value. -
Scope: Inside a module,
thisrefers to themodule.exportsobject. Theexportsvariable is a shorthand reference tomodule.exports, but reassigningexportsdoesn't affectmodule.exports—always usemodule.exportsfor full exports.
How It Works: Basic Example
Let's say we have two files: math.js (a module to export) and app.js (the main file that uses it).
math.js (exporting functions):
// Define some functions function add(a, b) { return a + b; } function subtract(a, b) { return a - b; } // Export an object with the functions exports.add = add; exports.subtract = subtract; // Or, equivalently, for the entire module: // module.exports = { add, subtract };
app.js (importing and using the module):
// Load the module synchronously const math = require('./math.js'); // Path relative to app.js // Use the exported functions console.log(math.add(5, 3)); // Output: 8 console.log(math.subtract(5, 3)); // Output: 2
- When
require('./math.js')is called, Node.js (or a CommonJS runtime) executesmath.jsonce, caches the result, and returns theexportsobject. - Subsequent
requirecalls for the same module return the cached value (no re-execution).
Exporting the Entire Module
You can export a single value (e.g., a class, function, or primitive) by assigning directly to module.exports:
// In greet.js function greet(name) { return `Hello, ${name}!`; } module.exports = greet; // Exports the function itself
Then import it:
const greet = require('./greet.js'); console.log(greet('World')); // Output: Hello, World!
Common Patterns and Gotchas
- Default Exports: CommonJS doesn't have "default" exports like ES modules; everything is named or the whole module.
- Circular Dependencies: If two modules require each other, CommonJS handles it by providing partial exports during loading, but this can lead to bugs—avoid if possible.
- Node.js Extensions: In Node.js, you can use
.mjsfor ES modules alongside CommonJS, or set"type": "module"inpackage.jsonto switch. - Third-Party Modules: Most npm packages use CommonJS, so
require('lodash')works out of the box.
| Aspect | CommonJS Example | Notes |
|---|---|---|
| Named Export | exports.foo = 42; |
Multiple per module |
| Full Export | module.exports = { foo: 42 }; |
Replaces entire exports |
| Import | const mod = require('./mod.js'); |
Synchronous, cached |
| Runtime | Node.js, Browserify, Webpack | Server-focused |
Pros and Cons
Pros:
- Simple and intuitive for backend code.
- Excellent caching prevents redundant execution.
- Wide adoption in the Node.js ecosystem.
Cons:
- Synchronous loading can block I/O in large apps (though rare in practice).
- Less flexible for tree-shaking (dead code elimination) compared to ES modules.
- Not native in browsers; requires bundlers for client-side use.
Relation to ES Modules
CommonJS predates ECMAScript (ES) modules (introduced in ES6). ES modules use import/export syntax and are asynchronous/static, making them better for browsers and modern bundlers. Many tools (e.g., Babel) can transpile between them. If you're starting a new project, consider ES modules for future-proofing, but CommonJS remains dominant in legacy Node.js code.
For hands-on practice, try running the examples in a Node.js environment (node app.js). If you have specific aspects (e.g., integration with npm or errors), let me know!