Understanding JavaScript Modules

Understanding JavaScript Modules Headers

One of the first pieces of JavaScript you write with Angular is a module. Modules have been around in JavaScript for a while and are not unique to Angular. However, the term is often used to mean different things depending on the context. Some developers refer to specific JavaScript coding patterns as modules, such as the ‘module pattern’. Some developers denote modules by using a popular module specification such as AMD or CommonJS. Maybe you have even heard of the upcoming EcmaScript® 2015 module specification. How do these concepts fit with Angular and its module system?

This is the first in a series of posts to demystify the term ‘module’ in JavaScript and Angular. The key to understanding JavaScript modules is that you can’t point to any one thing as a ‘module’. What developers are really doing is writing modular JavaScript and there are many different techniques and tools to achieve this goal. This post discusses the benefits of writing modular JavaScript and some of the general techniques and tools you can use to achieve these results.

Components Versus Modules

You will see the term component used throughout this post. While the terms component and module are often used interchangeably, this post uses the term component to refer to a piece of software encapsulating a single-responsibility. It is the what that you are trying to achieve. Using modules or writing modular JavaScript, is the how. Writing a module is the approach in which you achieve this composition. Because there are different ways to write modular JavaScript, the meaning of the term module changes accordingly.

A Note about Angular Modules

This module information focuses on pure JavaScript modules. There is information dedicated to Angular modules, specifically the Angular 1.x variety but there are two reasons to explore the wider world of JavaScript modules. The first reason is that there is a vast set of tooling available today for working with modular JavaScript that work well with Angular 1.x and extend the capabilities of the Angular 1.x module system. The second reason is that Angular 2 is aligning with the EcmaScript 2015 module standard in place of the module system for Angular 1.x. What you will see is that by increasing your ability to write modular JavaScript, you will increase the quality of your Angular code and workflow today and these skills will translate to Angular 2.

Benefits of Modular JavaScript

What developer does not want to work with more readable and maintainable code? This topic becomes the heart of many development team discussions. Writing modular JavaScript allows you to write code that is easier to maintain and debug. Here are some of the benefits:

Single Responsibility

If you are familiar with the SOLID principles, single-responsibility is important in code construction. Single responsibility means that your application’s components are each responsible for one purpose or function. By dividing your code in this manner and naming the components appropriately, you can more easily identify where code should be placed or where to begin to debug your code. Writing modular JavaScript allows you to create and separate these components from each other so that each module has a single responsibility.

Encapsulated Internals

Encapsulation is another important concept in software design meaning that your components may have data or functionality that is not meant to be accessed by other components. By encapsulating this information, you create a barrier preventing external components from calling these internal concerns. A popular way to implement encapsulation in JavaScript today is the use of closures. The EcmaScript 2015 module specification also defines ways to encapsulate pieces of JavaScript.

Avoids Global Scope

While this is similar to encapsulation, global scope collisions often occur at a higher level in an application. When libraries attempt to use the same global variables or two pieces of a large application are written to use the same global variables, this condition often causes problems. These types of collisions are becoming less frequent especially in popular third party libraries. Using module specifications further diminishes the likelihood of a global variable conflict.

Use Separate Code Files

Once your code is separated into individual components, separating that code into multiple files generally increases maintainability. Now changes to components are clearly identified in source control as changes to the associated files. The challenge is to ensure that all of the individual files are loaded into the browser correctly or combined into a single file. There are tools to help in this area including loaders and bundlers.

Manage Dependencies

Once you have code separated, you need to manage which pieces of code are dependent on another. There have been several specifications developed to help manage these dependencies. While these specifications differ in their syntax, they provide two fundamental pieces: a way to define dependent modules (imports) and a way to define pieces of a module that can be used in other modules (exports). Frameworks have developed around these specifications to intelligently load the necessary modules both in the browser and in NodeJS.

Achieving Modular JavaScript

Now that you know why you should write modular JavaScript, there are general techniques available to accomplish this. Here is an overview:

Patterns

There are numerous existing JavaScript patterns available to you today. You see these basic patterns in constructing your Angular controllers and services. If you ever use the ‘controller as’ syntax in Angular, you are essentially using the constructor pattern. EcmaScript 2015 introduces classes to JavaScript and you can use them to write controllers in Angular 1.x and components in Angular 2. If you are looking to deepen your understanding of current JavaScript patterns, check out Addy Osmani‘s book, Learning JavaScript Design Patterns. The other techniques for organizing JavaScript help but writing quality JavaScript code is the foundation on which you will build your application so don’t skip this step.

Module Specifications

Module specifications help manage dependencies in JavaScript. If you want to separate your code into components, how can you use (and re-use) these components throughout your application? You use a syntax to define the components available to your application (also known as exports) and a syntax to define which components are required as dependencies in another component (also known as imports). Having a specification helps ensure open source libraries work well together.

The main specifications in use today are Asynchronous Module Definition (AMD), CommonJS, and the upcoming EcmaScript 2015 module specification. The AMD specification is more often used in the browser whereas CommonJS is the spec of choice in NodeJS and NPM. The EcmaScript 2015 standard seeks to eventually become the standard supported by the major browser vendors and it has some nice options for defining imports and exports.

Bundlers

A straightforward way to load JavaScript files in the browser is to add a script element to the HTML page. However, there are several issues with this approach. First, there is performance overhead for sending individual requests to the browser for each file. Second, you have to manually add all of the separate script references on the page and make sure they are listed in the correct order.

By using a bundler, you can mitigate the performance problem by having a process run as part of a build (a Gulp task for instance) which combines all of the script files into one file. Thus, you can reference only one script in your page and the browser only has the overhead of making one HTTP request.

Furthermore, bundlers often minify your code as well making the variable names as small as possible and removing white space. Additionally, they create sourcemaps that you use to debug the code in the browser.

Finally, when you use a bundler in conjunction with a module specification, you no longer have to maintain a long list of scripts. The order of loading the scripts is inferred by the modules’ dependency tree. Browserify is a popular bundler that leverages the CommonJS module specification and JSPM works with EcmaScript 2015 modules.

Dynamic Loaders

Bundling every single JavaScript file together into one request may not make sense for very large applications. You may want to load a base set of libraries and then optionally load in other dependencies. You can accomplish this manually but you can also use a dynamic loader.

A dynamic loader is a JavaScript library that runs in the browser and helps your application download additional dependencies only when they are needed. They work in conjunction with module specifications so that you don’t have to manually call the loader’s API, the dependencies are inferred from the specified imports.

The choice to dynamically load scripts or bundle them is application-specific. Oftentimes, these tools are used in conjunction with each other to minimize page load times. Popular loaders are RequireJS using the AMD specification and SystemJS using EcmaScript 2015 modules.

Angular Modules

Specific to Angular 1.x is the Angular module system. This system provides you with much needed dependency injection in large JavaScript applications. However, it lacks the features you get combining loaders and bundlers with other module specifications. You can use Angular 1.x modules in conjunction with other module specifications to get the benefits of working with bundlers and loaders.

Angular 2 is moving towards using standards based module systems like EcmaScript 2015 making it much easier to use the latest module tooling.

JavaScript Compilers

With all the mentions in this post about EcmaScript 2015 modules, when will the browsers support them? Quite frankly, it doesn’t matter if you use a compiler (sometimes called a ‘transpiler’). JavaScript compilers understand future versions of JavaScript, and emit JavaScript that is compatible with today’s browsers. If you want to use EcmaScript 2015 today (JavaScript essentially is EcmaScript by the way), consider using a compiler. Popular JavaScript compilers are Babel, Traceur, and TypeScript.

In Summary

While the term JavaScript module might be difficult to define, the concepts are here to stay. Remember why writing modular JavaScript is important. You want components with single-responsibility and encapsulation. These components should stay out of the global scope. You also want the maintainability benefits of keeping your code in separate files and you want to leverage dependency injection to use components together in a loosely coupled fashion.

Once you understand why to write modular JavaScript, there are many tools you can leverage to write your modules. Take time to learn about today’s JavaScript patterns. Understand your options with module specifications and use bundlers, dynamic loaders, and compilers to put it all together in your application.

This post introduces a lot of topics and future posts will look at these concepts in more detail. If there is a concept in which you are particularly interested, please leave it in the comments.

One thought on “Understanding JavaScript Modules”

  1. Wow! Excellent article Aaron.

    I’ve been struggling to learn all of these concepts. You explain everything so well without getting religious or lost in the jargon plaguing many blog posts today.

    Looking forward to the follow posts.

    Maybe create a step by step code example for us mere mortal developers?

Comments are closed.