Mastering Front-End Module Systems: From IIFE to ES6 Modules

This article provides a comprehensive overview of front‑end modularization, covering the definition of modules, the evolution from early script tags through AMD/CMD to CommonJS and ES6, and explains why modern module syntax enables better dependency management and tree‑shaking.

ELab Team
ELab Team
ELab Team
Mastering Front-End Module Systems: From IIFE to ES6 Modules

Front-End Modularity Overview

Introduction

This article discusses front‑end modularization, focusing on what modularity is, why it is needed, the pros and cons of various module specifications, and the historical evolution of these specifications to give readers a clearer understanding of modular programming.

What Is Modularity?

A module is a portion of code packaged according to certain rules, forming a dependency relationship with other modules. Internally defined modules belong to the project, while external packages imported from npm are considered external modules.

Management of External Modules

In 2009 Node.js extended JavaScript to the backend, and a year later npm (Node Package Manager) revolutionized how external modules are used.

Using External Modules Before npm

Before npm, developers had to download library files manually and include them via <script> tags, which was inconvenient, lacked version management, and made tracking sources difficult.

Using External Modules After npm

npm provides a remote JavaScript repository; developers can install packages with npm install <module-name>, which stores them in a node_modules directory and updates package.json with version information.

Management of Internal Modules

The article then focuses on internal module management, from native JavaScript patterns (IIFE) to specifications such as CommonJS, AMD, and finally ES6.

Native JavaScript Organization

Function Pattern

function plusOne() {
  // ...
}

function minusOne() {
  // ...
}

This approach pollutes the global namespace, leading to naming conflicts and unclear dependencies.

Object Pattern

var calculator = {
  plusOne() {
    // ...
  },
  minusOne() {
    // ...
  }
};

While reducing global variables, the object pattern still exposes internal data to external modification.

IIFE Pattern

var calculator = (function(){
  var count = 0;
  function plusOne(){ count++; }
  function minusOne(){ count--; }
  function printCount(){ console.log(count); }
  return { plusOne, minusOne, printCount };
})();

calculator.plusOne();
calculator.printCount(); // 1

The IIFE creates a private scope, exposing only the public API.

Global Object Exposure (e.g., jQuery)

(function(window){
  // ...
  window.jQuery = window.$ = jQuery;
})(window);

Relying on global objects can cause maintenance issues as projects grow.

Modular Specifications

Various specifications emerged to solve the problems of native script organization.

AMD & CMD

Older specifications like AMD (Asynchronous Module Definition) and CMD (Common Module Definition) are now largely deprecated.

define : defines a module

require : uses a module

Typical tools: RequireJS (AMD) and Sea.js (CMD).

CommonJS

CommonJS is the module system used by Node.js. Each file is treated as an independent module, wrapped in an IIFE that provides exports, require, and module objects.

(function(exports, require, module, __filename, __dirname){
  // module code
});

Modules export objects via module.exports and are imported with require.

const EventEmitter = require('events');
module.exports = new EventEmitter();
setTimeout(() => {
  module.exports.emit('ready');
}, 1000);
const a = require('./a');
a.on('ready', () => {
  console.log('module "a" is ready');
});

ES6 Modules

ES6 modules are designed to be static; their dependencies are known at compile time, enabling features like tree‑shaking.

Modules can only appear at the top level.

Import specifiers must be string literals.

Import bindings are immutable.

Static analysis (lexical analysis, parsing, code generation) allows tools to determine module relationships without executing code.

import foo from './foo';
import bar from './bar';

Because imports are static, bundlers such as Rollup and Webpack can eliminate unused code (tree‑shaking).

Conclusion

The evolution from script tags to npm, AMD/CMD, CommonJS, and finally ES6 modules reflects the maturation of front‑end development. Modern tooling like Babel enables developers to write ES6 code while maintaining compatibility with older browsers.

References

RequireJS: https://requirejs.org/

Sea.js: https://seajs.github.io/seajs/docs/

RequireJS creates async script tags: https://github.com/requirejs/requirejs/blob/master/require.js#L1875

CommonJS specification: http://nodejs.cn/api/modules.html

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.

module systemCommonJSAMDes6RequireJS
ELab Team
Written by

ELab Team

Sharing fresh technical insights

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.