A Student's Guide to Software Engineering Tools & Techniques »

Javascript: Modules

Author: Gilbert Emerson
Reviewers: Chelsey Ong, Ong Shu Peng, Amrut Prabhu

This article assumes the reader has some basic knowledge of JavaScript.

What is a Module?

In programming, the term module (other similar terms: package, library, dependency, plugin) is used to refer to a small part of code that is broken up from a larger code base.

Modules help programmers in many ways. Here are some of the examples:

1. Modules make code managable
Using modules to break a code base into smaller parts can make it more manageable, especially for a large code base.

Let's say you have an application with functionalities A and B, where functionality A needs functionality B. Without modules, both of these functionalities are mixed together in the code base without a clear separation. With modules, we can separate these 2 functionalities. When A needs B, A will "include" B and A will be able to work as if A and B had never been separated.

2. Modules help minimize name clashes
Breaking code into modules results in breaking the code's namespace into smaller parts too. This will help in minimizing name clashes and the need for global variables.

3. Modules promote reuse
Modules allow developers to reuse their code. If we have an application that relies on the same string comparison function in multiple files, we can separate that function into a module. Now we can "include" the function from that module instead of repeating that function definition in all the files where it is needed.

How to Modularize JavaScript Code?

There are 3 common ways to use modules in JavaScript: 1. using ES6 modules, 2. using CommonJS, 3. using the module pattern. While ES6 modules is the most recent and the official implementation, this article covers the other two as well because there are still a large number of existing projects that use them.

ES6 Modules

Introduced in 2015, ES6 modules is the official implementation of modules in JavaScript. It introduces 2 new syntax import and export to support modules in JavaScript.

In the example below, index.js needs the function sumOfVariable from anExampleModule.js. So, anExampleModule.js will need to export the function using the export syntax and index.js will need to import that function using the import syntax.

// anExampleModule.js

var variableOne = 1;
var variableTwo = 2;

export sumOfVariable() {
    return variableOne + variableTwo;
}
// index.js

import * as anExampleModule from './anExampleModule.js';

anExampleModule.sumOfVariable(); // 3

ES6 modules supports advanced features such as asynchronous loading, tree shaking, static code analysis and dynamic imports. These features will not be covered in this article.

A more in-depth explanation of ES6 modules can be found in the Modules chapter of the Exploring ES6 online book.

ES6 modules is supported by all major browsers as of 2020. However, you may need to support browsers that do not implement ES6 modules.

One possible solution is to use a transpiler such as Babel and a bundler such as Webpack to serve your application to unsupported browsers.

Alternatively, use the two other methods described below.

CommonJS

CommonJS is in wide use today because it is used by NodeJS which in turn is used by many JavaScript applications.

The example below will replicate the same example in the previous section using CommonJS. index.js needs the function sumOfVariable from anExampleModule.js. So, anExampleModule.js module will need to export the function using the module.exports syntax and index.js module will need to import that function using the require syntax.

// anExampleModule.js

var variableOne = 1;
var variableTwo = 2;

sumOfVariable = function() {
    return variableOne + variableTwo;
}

module.exports = {
  sumOfVariable: sumOfVariable,
};
// index.js

var anExampleModule = require('./anExampleModule.js');

anExampleModule.sumOfVariable(); // 3

A more in-depth explanation of CommonJS can be found in the Modules chapter of NodeJS API documentation.

Note that CommonJS modules only work natively in NodeJS. If you would like to use CommonJS modules in browsers, you will still need to use a bundler such as Webpack.

If you are only writing JavaScript for the client side and not using NodeJS at all, and also need to support a wide variety of browsers, consider using the last method described. It has been supported by browsers ever since they started supporting JavaScript.

Module Pattern via Namespaces

Using a technique in JavaScript called IIFE (Immediately Invoked Function Expression), JavaScript developers can create a namespace by wrapping their code in an IIFE.

An IIFE (Immediately Invoked Function Expression) is a JavaScript function that runs as soon as it is defined.
Syntax: (function() { statements })();

Source: MDN Glossary - IIFE

The example below will replicate the same example in previous sections using the module pattern. index.js needs the function sumOfVariable from anExampleNamespace.js. To achieve this, we use the <script> tag in HTML to include anExampleNamespace.js for index.js to use.

<!-- index.html -->

<script src="./anExampleNamespace.js" />
<script src="./index.js" />
// anExampleNamespace.js

var anExampleNamespace = (function() {
    var variableOne = 1;
    var variableTwo = 2;

    var sumOfVariable = function() {
        return variableOne + variableTwo;
    }

    // We return an object with functions or variables
    // that we want to be exposed
    return {
        sumOfVariable: sumOfVariable
    }
})()
// index.js

anExampleNamespace.sumOfVariable(); // 3

The result of including the script containing the anExampleNamespace object in the HTML will be as follows:

// anExampleNamespace.js

var anExampleNamespace = (function() {
    var variableOne = 1;
    var variableTwo = 2;

    var sumOfVariable = function() {
        return variableOne + variableTwo;
    }

    // We return an object with functions or variables
    // that we want to be exposed
    return {
        sumOfVariable: sumOfVariable
    }
})()

// index.js

anExampleNamespace.sumOfVariable(); // 3

A more in-depth explanation of the module pattern can be found in the this course blog on mastering module pattern.

Which to Use?

Although ES6 modules is the official way to implement modules, there are situations where you might have to use one of the other options. Here are some examples:

  • If your application does not allow you to use a transpiler or a bundler (e.g. because of the additional overhead they add), you can use the module pattern via namespaces.
  • If your application is using Node.js, you might want to use CommonJS because Node.js only has experimental support for ES6 modules as of Node.js 13.8.0.

How to Start?

You can use the module pattern right away by refactoring segments of your code into different files and wrapping your code in an IIFE.

If you are planning to use CommonJS or ES6 modules, you can start by refactoring segments of your code, but you will also need to be familiar with transpilers and bundlers such as Babel and Webpack.

Further Reading

You can read more on JavaScript modules at following websites: