Understanding JavaScript Module Systems: From Require to ES6 Imports

This article explains the evolution of JavaScript module patterns—including CommonJS, AMD, CMD, and ES6—by illustrating their syntax, loading behavior, and practical drawbacks, and shows how to simulate modularity with simple global objects and closures.

ITFLY8 Architecture Home
ITFLY8 Architecture Home
ITFLY8 Architecture Home
Understanding JavaScript Module Systems: From Require to ES6 Imports

Preface

The keywords require and import introduce module concepts such as CommonJS, CMD, AMD, RequireJS, and SeaJS, which can be confusing for frontend beginners.

JavaScript Basics

Compared with import statements in Objective‑C, Swift, and Java, JavaScript’s dynamic nature changes how modules work.

<html>
  <head>
    <script type="text/javascript" src="index.js"></script>
  </head>
  <body>
    <p id="hello"> Hello Wrold </p>
    <input type="button" onclick="onPress()" value="Click me" />
  </body>
</html>
function onPress() {
    var p = document.getElementById('hello');
    p.innerHTML = 'Hello bestswifter';
}

The <script> tag works like an import, allowing the button’s onclick handler to call onPress defined in index.js.

If the displayed text must be generated dynamically by another file, a simple index.js is insufficient. Suppose the logic resides in math.js:

function add(a, b) {
    return a + b;
}

Attempting to write index.js with an ES‑style import fails because JavaScript does not support that syntax natively:

import "math.js"
function onPress() {
    var p = document.getElementById('hello');
    p.innerHTML = add(1, 2);
}

Instead, both scripts must be referenced in the HTML:

<html>
  <head>
    <script type="text/javascript" src="index.js"></script>
    <script type="text/javascript" src="math.js"></script>
  </head>
  <body>
    <p id="hello"> Hello Wrold </p>
    <input type="button" onclick="onPress()" value="Click me" />
  </body>
</html>
function onPress() {
    var p = document.getElementById('hello');
    p.innerHTML = add(1, 2);
}

This approach relies on the correct loading order of HTML scripts, which is fragile.

Initial Modularization

The main pain points are:

index.js cannot import modules directly and depends on HTML.

index.js lacks a namespace, making it impossible to distinguish the origin of functions like add.

One quick fix is to expose a simple namespace object:

function onPress() {
    var p = document.getElementById('hello');
    p.innerHTML = math.add(1, 2);
}
var math = {
    base: 0,
    add: function(a, b) { return a + b + base; }
};

To hide internal state, wrap the module in a closure:

var math = (function() {
    var base = 0;
    return {
        add: function(a, b) { return a + b + base; }
    };
})();

For a more Node‑like experience, introduce a global module object to store exports: var module = { exports: {} }; Expose the math module:

var math = (function() {
    var base = 0;
    return {
        add: function(a, b) { return a + b + base; }
    };
})();
module.exports.math = math;

Consume it in index.js:

var math = module.exports.math;
function onPress() {
    var p = document.getElementById('hello');
    p.innerHTML = math.add(1, 2);
}

Existing Modular Solutions

The simple approach still suffers from manual load order management, lack of asynchronous loading, and namespace collisions. Various standards have emerged to address these issues.

CommonJS

CommonJS, used by Node.js, loads modules synchronously, which works on the server but not in browsers where scripts must load asynchronously.

AMD

RequireJS implements the Asynchronous Module Definition (AMD) pattern, loading modules via importScripts and executing a callback after all dependencies are ready.

require(['myModule1', 'myModule2'], function (m1, m2) {
    // main logic
    m1.printName();
    m2.printName();
});

All listed modules are fetched in parallel, and the callback runs only after they have been loaded.

CMD

Sea.js follows the Common Module Definition (CMD) pattern, allowing dependencies to be declared near their usage and supporting both synchronous and asynchronous loading.

define(function (require, exports, module) {
    var foo = require('foo'); // sync
    foo.add(1, 2);
    // async loading
    require.async('math', function (math) {
        math.add(1, 2);
    });
});

CMD loads modules in the order they appear in the source, preserving a predictable execution sequence.

ES6 Modules

ES6 introduces native export and import statements. Because most browsers still lack full support, tools like Babel transpile import to require. Nevertheless, using the ES6 syntax is encouraged for future‑proof code.

References

Can I access variables from another file?

浅谈 JavaScript 模块化编程

前端模块化

详解JavaScript模块化开发

requireJS实现原理研究

Javascript模块化编程(一):模块的写法

Javascript模块化编程(二):AMD规范

浏览器加载 CommonJS 模块的原理与实现

Node中没搞明白require和import,你会被坑的很惨

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

frontendJavaScriptCommonJSmoduleAMDes6
ITFLY8 Architecture Home
Written by

ITFLY8 Architecture Home

ITFLY8 Architecture Home - focused on architecture knowledge sharing and exchange, covering project management and product design. Includes large-scale distributed website architecture (high performance, high availability, caching, message queues...), design patterns, architecture patterns, big data, project management (SCRUM, PMP, Prince2), product design, and more.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.