o7planning

The History of Modules in JavaScript

  1. Module?
  2. IIFE
  3. CommonJS
  4. AMD
  5. UMD
  6. ES Modules

1. Module?

This article introduces you to the module history in JavaScript. Many specifications and module implementations were created until JavaScript ES6 (2105) released a new module specification that was supported at the language level. Therefore, the old specifications are slowly becoming obsolete. Although some of them are still in use somewhere, such as NodeJS, but they will certainly be phased out in the near future.
The purpose of this article is to provide an overview and eliminate confusion for those who are just getting started with JavaScript Module, and avoid wasting time learning outdated libraries.
JavaScript programs started off pretty small — most of its usage in the early days was to do isolated scripting tasks, providing a bit of interactivity to your web pages where needed, so large scripts were generally not needed. Fast forward a few years and we now have complete applications being run in browsers with a lot of JavaScript, as well as JavaScript being used in many different contexts, such as NodeJs.
As JavaScript started to be used more in applications, it became more difficult to manage and maintain the code. In addition, using multiple JavaScript files in the same program will cause variable or function name conflicts, and create great risks. Take a look at the following simple example:
file_1a.js
var friendName = "Tom";
function sayHello() {
    console.log(`Hello ${friendName}!`);
}
file_2a.js
sayHello(); // Hello Tom!
friendName = 'Jerry';
sayHello(); // Hello Jerry!
test_a.html
<html>
  <head>
     <script src="file_1a.js"></script>
     <script src="file_2a.js"></script>
     <script>
        var friendName = 'Donald';
        sayHello(); // Hello Donald!
     </script>
  </head>
  <body>
      View the result in the Browser Console Window.
  </body>
</html>
Output:
Hello Tom!
Hello Jerry!
Hello Donald!
Thus the module concept emerged to solve two important problems:
  • The module must be an enclosed space, all that is shared to the outside must originate with a clear intention.
  • Modules should be a way to divide a large application into many separate small parts, making it easier for programmers to develop and maintain the application.

2. IIFE

IIFE (Immediately Invoked Function Expression): This is the most primitive technique for modularizing code. At this time, the concept of classes does not exist in JavaScript. Using IIFE gives us the freedom to create variables or functions in a closed space for internal use, and without fear of conflict anywhere else in the application.
The IIFE expression looks like this:
(function () {
  // Statements
})();
It looks like a brain hack because a lot of brackets are involved in the expression, but the equivalent form below is definitely easier to understand:
var myFunc = function()  {
   // Statements
}  
myFunc();
IIFE helps to hide the code being processed inside and exposes only what we want. Example:
var text = (function () {
    var privateText = "My private TEXT";
    var publicText = "My public TEXT";
    return publicText;
})();

console.log(text); // My public TEXT
And we can still access global variables from within the IIFE normally:
var myGlobalVariable = 'Hello, I am a global variable :)';

var text = (function () {
    var privateText = "My private TEXT";
    var publicText = "My public TEXT";
    console.log(myGlobalVariable);
    return publicText;
})();

console.log(text); // My public TEXT
Continuing with the idea above, an IIFE example returns an object structured as an interface:
file_1b.js
var mySimpleModule = (function() {
   var greeting = 'Hello'; // Private variable!
   var friendName = "Tom";
   var sayHello = function()  {
       console.log(`${greeting} ${friendName}!`);
   }
   var mm = {
       friendName: friendName,
       sayHello: sayHello
   };
   return mm;
})();
file_2b.js
// Call sayHello function of 'mySimpleModule':
mySimpleModule.sayHello(); // Hello Tom!

var friendName = 'Donald';
mySimpleModule.friendName = 'Jerry';
mySimpleModule.sayHello(); // Hello Jerry!

3. CommonJS

CommonJS is a specification started in January 2009 by Mozilla engineer Kevin Dangoor. Its main purpose is to establish module ecosystem conventions for JavaScript environments other than those provided by the browser. We're in the middle now, as the illustration below, right?
Actually, the project was originally named ServerJS, aiming for the JavaScript ecosystem at the server. In August 2009 it was renamed to CommonJS as an expression of its wide range of uses, including browsers.
Since its inception, the CommonJS module specification has been used for NodeJS. And until this article was published (2021), CommonJS was still used in NodeJS. However, it is certain that it will be replaced by the ES6 Module in the near future. The identifying characteristics of CommonJS are the require() and module.exports() functions.
CommonJS advantages:
  • Simple and easy to use without looking at the documentation.
  • Built-in dependency manager. Modules are loaded in an appropriate order.
  • The require() function can be used anywhere to require another module.
  • Support circular dependency.
Disadvantages of CommonJS:
  • Loading modules synchronously causes slow loading speed and is not suitable for certain uses, such as browser-side applications.
  • One file per module.
  • Browsers don't understand modules according to the CommonJS standard. It needs an additional Module Loader library or at least it must be transpiled by a transpiler.
  • Does not support constructor functions (As specification). However, NodeJS supports this.
  • Difficult to parse for static code parsers.
JavaScript libraries that implement the CommonJS specification:
  • NodeJS (Server side)
  • webpack (Client side)
  • Browserify (Client side)
Example: How to declare a module in CommonJS:
cjs_file_1.js
function _add(a, b) {
    return a + b;
}
function _subtract(a, b) {
    return a - b;
}
module.exports = {
    add: _add,
    subtract: _subtract,
};
Usage:
cjs_file_2.js
// Using require() function to load file "csj_file_1.js", no need .js extension.
var calculator = require("./csj_file_1");
console.log(calculator.add(2, 2)); // 4
console.log(calculator.subtract(2, 2)); // 0
See in-depth CommonJS articles:
  • JavaScript CommonJS

4. AMD

AMD (Asynchronous Module Definition) was born out of a group of developers that were displeased with the direction adopted by CommonJS. In fact, AMD was split from CommonJS early in its development. The main difference between AMD and CommonJS lies in its support for asynchronous module loading. Like CommonJS, AMD is just a specification.
With asynchronous loading, libraries that do not depend on each other for loading can be loaded at the same time. This is especially important for browsers, where startup time is essential for a good user experience.
AMD advantages:
  • Asynchronous loading, better startup time.
  • Supports and is compatible with require() and module.exports() functions, similar to CommonJS.
  • Built-in dependency manager.
  • A module can include multiple files.
  • Support constructor function.
  • Support circular dependency.
  • Plugin support (custom loading steps).
Disadvantages of AMD:
  • Syntactically, it's a bit more complicated than CommonJS.
  • Difficult to parse for static code parsers.
  • Browsers don't understand modules according to AMD standards. It needs an additional Module Loader library or at least it has to be transpiled by a transpiler.
Example: Create an AMD module:
amd_file_1.js
define("myAmdModule", {
    add: function (a, b) {
        return a + b;
    },
    subtract: function (a, b) {
        return a - b;
    },
});
amd_file_2.js
require(["myAmdModule"], function (myAmdModule) {
    console.log(myAmdModule.add(2, 2)); // 4
    console.log(myAmdModule.subtract(2, 2)); // 0
});
See in-depth AMD articles:
  • JavaScript AMD

5. UMD

UMD (Universal Module Definition) is a set of techniques used to create modules that can be imported as IIFE, CommonJS or AMD modules. Therefore, a program can now import third-party modules, regardless of which module specification it is using.
Example:
UMD_file_1.js
(function (myUmdModule) {
    if (typeof define === 'function' && define.amd) {
        // Export with AMD
        define("myUmdModule", myUmdModule);
    } else if (typeof module === 'object' && module.exports) {
        // Export with CommonJS
        module.exports = myUmdModule;
    } else {
        // Export with browser global
        window.myUmdModule = myUmdModule;
    }
}({
    add: function (a, b) {
        return a + b;
    },
    subtract: function (a, b) {
        return a - b;
    },
}));
UMD_file_2.js
// Load with AMD
require(["myUmdModule"], function (myUmdModule) {
    console.log(myUmdModule.add(2, 2));
    console.log(myUmdModule.subtract(2, 2));
});
// Load with CommonJS
var myUmdModule = require("./myUmdModule");
console.log(myUmdModule.add(2, 2));
console.log(myUmdModule.subtract(2, 2));

6. ES Modules

ES Modules aka ECMAScript Modules or ES6 Modules or ES2015 Modules. ES Modules is the solution for a bright future of the module ecosystem by standardizing module declaration syntax, working on both Backend and Frontend, and supported by JavaScript at the language level. However, not all users have migrated to modern browsers that support ES6, so it needs more time.

ECMAScript, Javascript Tutorials

Show More