The Evolution of JavaScript Module  Patterns

Practical examples of JavaScript module patterns and how to include them as a dependency.

The evolution of JavaScript module patterns has been a huge headache for web developers. Attempting to work with plugins and libraries that all use different module patterns overly difficult, but once you understand each pattern and how to work with it you can begin to translate and make sense of each. This article should provide a good source of example JavaScript module patterns, both how to create a module and how to include it as a dependency.

Overview

Each module example below will do the exact same thing: a console.log will send the message, “Hi my name is Kevin.”, to the browser console. I’ve ordered these in terms of their evolution, starting with the oldest patterns.

IIFE (Immediately-Invoked Function Expression)

It all started here, this is the oldest form of a module in JavaScript.

An Immediately Invoked Function Expression (IIFE) is an anonymous function that is invoked when it is declared. It isn’t really a module pattern, it’s an enclosure pattern that helps encapsulate code and keep it isolated from other parts of an application.

Module Creation

// Module
(function(){
  console.info( 'Hi my name is Kevin.' );
})()

Dependency Use

Include the IIFE “module” in your HTML document as a <script>. This is the older pattern out there but is still used today.

<script src="module.js"></script>

AMD (Asynchronous Module Definition)

This pattern is used in browsers and makes use of a define function to define modules, and a require function to include a module as a dependency. The require function is different than the Node.js pattern that most developers are familiar with, and this is the source of a lot of confusion. require in the context of AMD dates back to an old library called require.js that would load a separate JS file in asynchronously for browser use. This eventually led to the design of the CommonJS pattern, which is the next stage of evolution.

Module Creation

define( 'module', function () {
   return {
      log: function () {
         console.info( 'Hi my name is Kevin.' );
      }
   };
});

Dependency Use

require(['module'], function( module ) {
  module.log();
});

CommonJS

This pattern evolved from the browser-based AMD pattern for use in Node.js on the server-side. It uses module.exports to define modules, and require('...') to include them as a dependency.

Module Creation

// Module
module.exports = {
  log: function() {
    return console.log( 'Hi my name is Kevin.' );
  }
}

Dependency Use

const module = require('./module');
module.log();

UMD (Universal Module Definition)

The UMD pattern emerged next as a way to support both Node.js and browsers. To do this it will conditionally check how a module was included as a dependency, and then choose from 3 approaches to include it as a module: AMD, Common.js, and browser globals. This became popular amongst open-source plugin creators and was intended to provide a way for them to support many use cases with their libraries.

Module Creation

(function( root, factory ) {
  // AMD
  if (typeof define === 'function' && define.amd) {
    define( ['module'], factory );
  }
  // CommonJS/Node.js
  else if ( typeof module === 'object' && module.exports ) {
    module.exports = factory(require('module'));
  }
  // Browser globals
  else {
    // root = window
    root.returnExports = factory(root.b);
  }
}(this, function( module ) {
  return {
   log: function() {
     return console.log( 'Hi my name is Kevin.' );
   }
  };
}));

Dependency Use

There are 3 ways to work with a UMD module:

// AMD
require(['module'], function( module ) {
  module.log();
});

// CommonJS
const module = require('./module');
module.log();

// Browser globals
const module = window.module;
module.log();

SystemJS & ES6 module’s

The next stage in evolution, and the most modern and best case module pattern to use today, as of 2020, is known as just System, System.register or simply ES6 modules. This makes use of the syntax import ... from './module'. It was designed to support the ES6 module syntax in ES5 JavaScript environments. The syntax is basically the same as the ES6 module syntax, so I’ve included these examples together here for simplicity. This pattern is typically used as the gold standard in an application built with TypeScript.

Module Creation

// Module
export default {
  log: function() {
    return console.log( 'Hi my name is Kevin.' );
  }
}

Dependency Use

import { log } from "./module";
log();

Meet the Author

Kevin Leary, WordPress Consultant

I'm a freelance web developer and WordPress consultant in Boston, MA with 17 years of experience building websites and applications. View a portfolio of my work or request an estimate for your next project.