Mastering JavaScript Module Systems: From CommonJS to ES6 and Beyond
This comprehensive guide explores JavaScript module mechanisms—including CommonJS, Node.js core and file modules, AMD, CMD, UMD, and ES6 import/export—detailing their advantages, loading strategies, code examples, circular dependencies, and best practices for modern front‑end and back‑end development.
Frontend Enthusiasts' Knowledge Feast
Frontend enthusiasts' knowledge feast.
Advantages of Modules
Maintainability : Because a module is independent, a well‑designed module minimizes external dependencies, allowing it to be updated and improved in isolation.
Namespace : In JavaScript, variables declared outside the top‑level function become global, which can cause naming conflicts; using modules encapsulates variables and prevents global pollution.
Code Reuse : Instead of copying code between projects, modules can be referenced, eliminating duplicate code bases.
CommonJS
CommonJS was initiated by Mozilla engineers in 2009 to enable modular development for environments outside the browser (e.g., server‑side or desktop). In the CommonJS specification each JavaScript file is an isolated module context; all variables, functions, and classes defined inside are private and invisible to other files.
The specification is primarily suited for server‑side code and uses synchronous loading. When three modules are required, they are loaded one after another.
The core of the implementation consists of the require and module keywords, which allow a module to expose part of its interface and let other modules import it.
// sayModule.js
function SayModule() {
this.hello = function () {
console.log('hello');
};
this.goodbye = function () {
console.log('goodbye');
};
}
module.exports = SayModule;
// main.js
var Say = require('./sayModule.js');
var sayer = new Say();
sayer.hello(); // helloAs a server‑side solution, CommonJS requires a compatible script loader that provides the require and module.exports functions.
Node.js
Node.js builds on some ideas from CommonJS; because of its popularity on the server, the Node module system is often (incorrectly) referred to as CommonJS.
Node.js modules are divided into two categories: core modules (e.g., fs, http, net) provided by the Node runtime, and file modules (JavaScript, JSON, or compiled C/C++ files). When a module is required without an explicit extension, Node tries .js, .json, and .node in that order.
Modules are loaded by path:
If the require argument starts with /, an absolute path is used.
If it starts with ./ or ../, a relative path is resolved.
If it has no leading slash, Node first looks for a core module; if none is found it searches node_modules directories.
Node caches loaded file modules by filename, so subsequent requires return the same instance (modules are singletons). The cache is based on the resolved filename, not the string passed to require.
The loading process is implemented by the native module module. When a file module is loaded, Node wraps its content in a function like:
(function (exports, require, module, __filename, __dirname) {
// module code
});Node also supports native addons ( .node) which are loaded via process.dlopen.
AMD (Asynchronous Module Definition)
AMD is an asynchronous module definition format designed for browsers. It loads modules asynchronously and executes a callback once all dependencies are available.
// lib/sayModule.js
define(function () {
return {
sayHello: function () {
console.log('hello');
}
};
});
// main.js
define(['./lib/sayModule'], function (say) {
say.sayHello(); // hello
});AMD requires a script loader (e.g., RequireJS) that understands the define function.
RequireJS
RequireJS is a front‑end module loader that follows the AMD specification. It loads modules via a data-main script tag and executes the main module after all dependencies are resolved.
<script data-main="scripts/main" src="scripts/require.js"></script>CMD (Common Module Definition)
CMD, used by SeaJS, treats each file as a module. Modules are defined with a global define function. The factory can be a function, object, or string.
// define a JSON module
define({ "foo": "bar" });
// define a functional module
define(function (require, exports, module) {
// module code
});SeaJS
SeaJS follows the CMD specification and provides a simple seajs.use API to load one or multiple modules.
// Load a single module
seajs.use('./a');
// Load multiple modules with a callback
seajs.use(['./a', './b'], function (a, b) {
a.doSomething();
b.doSomething();
});AMD loads dependencies before executing the module, while CMD loads them lazily, preserving the order of execution as written in the source.
UMD (Universal Module Definition)
UMD combines AMD and CommonJS so that a module works in both environments. A typical pattern checks for module.exports (CommonJS) and define (AMD) before falling back to a global variable.
(function (define) {
define(function () {
return {
sayHello: function () {
console.log('hello');
}
};
});
})(typeof module === 'object' && module.exports && typeof define !== 'function' ?
function (factory) { module.exports = factory(); } :
define);ES6 Modules
Strict Mode
ES6 modules are always in strict mode, which enforces rules such as variable declaration before use, no duplicate function parameters, no with statements, and many other restrictions.
Export
A module can export variables, functions, or objects. Named exports keep their original names unless renamed with as. Only one default export is allowed per module.
// profile.js
export var firstName = 'qiqi';
export var lastName = 'haobenben';
export var year = 1992;
// Equivalent explicit export
var firstName = 'qiqi';
var lastName = 'haobenben';
var year = 1992;
export { firstName, lastName, year };Exports can be renamed:
export { v1 as streamV1, v2 as streamV2 };Import
Import statements bring exported bindings into the current module. They are hoisted and executed before any other code.
import { lastName as surename } from './profile';
import defaultFn from './module-B.js';
import * as utils from 'lodash';Import statements cannot contain expressions or be placed inside conditional blocks because they are static.
Default Export
A module can export a single default value, which can be imported without curly braces.
export default function () {
console.log('I am default Fn');
};
import sayDefault from './module-B.js';
sayDefault(); // I am default FnCombined Export/Import
export { foo, bar } from './my_module';
// Equivalent to
import { foo, bar } from './my_module';
export { foo, bar };Circular Dependencies
ES6 imports are live read‑only views of the exported bindings, so circular dependencies work as long as the modules only use the exported values after they are initialized.
// a.js
import { bar } from './b';
export function foo() { bar(); }
// b.js
import { foo } from './a';
export function bar() { if (Math.random()) { foo(); } }When a.js is loaded first, bar is a reference to the function exported by b.js, and the circular call works correctly.
Batch Export / Import
// module-B.js
export var name = "cfangxu";
export var age = 26;
export function say() { console.log('say hello'); }
// Importing all at once
import * as obj from './module-B.js';
console.log(obj.name); // cfangxu
console.log(obj.age); // 26
obj.say(); // say helloOnly one default export is allowed per module, which is why the import syntax for a default export does not use curly braces.
Okay, this JavaScript modularization summary is complete. If you liked the article, feel free to leave a comment or share it.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Tencent IMWeb Frontend Team
IMWeb Frontend Community gathering frontend development enthusiasts. Follow us for refined live courses by top experts, cutting‑edge technical posts, and to sharpen your frontend skills.
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.
