Posted on by & filed under Content - Highlights and Reviews, Programming & Development.

A guest post by Ben Hockey, a Senior Software Developer at Sitepen, living in Nashville, TN with his wife and 3 kids. At SitePen, he builds Web-based Applications using The Dojo Toolkit.

Back in 2009 I was part of a discussion with other Dojo developers, where we were brainstorming a new module format for Dojo. At the same time, another group of developers were discussing modules as part of the CommonJS initiative. James Burke tried to bring the discussions together by proposing a module format that he had hoped both groups would find desirable. After much discussion, the module format he proposed took on a life of it’s own. The format was refined over time and today it is known as Asynchronous Module Definition (AMD).

There are a number of loaders that implement AMD but James Burke’s RequireJS is the original and many people are now so familiar with RequireJS that they are unaware of the distinction between the AMD format and the RequireJS loader. You can read more about the history of RequireJS from James but let’s look at why AMD and RequireJS should matter to you as a JavaScript developer.

Modulo Modular

JavaScript has been criticized by “serious developers” as being a toy language. Some of the criticisms are likely because JavaScript lacks some of the familiar characteristics that have provided a sense of comfort to these serious developers – things like compilation, strong typing, and classical inheritance. I don’t agree that the lack of these mean that JavaScript deserves to be called a toy language, but maybe one great contributor to the criticism is the amount of poorly structured code that has been written in JavaScript.

AMD is a format that provides a way to write modular code, despite the lack of native modules in JavaScript. Writing modular code helps improve the quality of code by providing:

  • encapsulation
  • code reuse
  • reduced reliance on globals
  • separation of concerns
  • testability

RequireJS is a loader that understands the AMD format, so let’s look at how to write AMD modules and use RequireJS to load them.

Using RequireJS

Getting started with RequireJS is as simple as including a single script tag in your HTML page and then writing your modules using define to declare dependencies and export functionality.

The RequireJS documentation shows a number of ways to use define, but probably the two most common forms are described in that documentation as:

  1. Definition Functions with Dependencies
  2. Simplified CommonJS Wrapper

Let’s compare both formats by looking at the code for an app/init module that exports an initialization function that accepts an array of users and constructs a model, view and controller to present that data.

The code is based on the following directory structure:

TIP: In these examples, we have followed the best practices of:

  • Using relative module IDs for our dependencies.
  • Writing anonymous modules – i.e. the id of the module has not been included in the call to define.

Both of these make our code more portable – i.e. app could be moved or renamed and our code will still work without any refactoring.

Definition Functions with Dependencies

With this format, an array is passed as the first argument to define. This array is a list of module IDs that represent our module’s dependencies. The 2nd argument passed to define is known as the module’s factory or definition function, and will be called when the dependencies have been fully resolved.

To resolve the dependencies, RequireJS will map the list of module IDs to URLs and load those modules via script injection. When the module’s definition function is called, it is passed the exports of each of the requested dependencies.

TIP: The arguments passed to the definition function are in the same order that they are listed in the dependencies array.

Simplified CommonJS Wrapper

For developers with experience using Node.js, this format should look very familiar to you. The body of the definition function is just like the contents of a CommonJS module as used in Node.js. You can use require to get a reference to your dependencies, and your module is able to export functionality via exports or module.exports.

Given that dependencies are resolved asynchronously, and this format involves synchronous calls to require to get references to the exports of a module, RequireJS determines the dependencies before calling the definition function when using this format. The dependencies are determined by scanning the definition function using Function.prototype.toString and applying a regular expression to find all occurrences that would appear to be a call to require('dependency'). Once the definition function has been scanned and the dependencies have been resolved, the definition function is called with the following arguments:

  • require – a context sensitive require that can be used to resolve dependencies.
  • exports – an empty object; the initial value exported by a module.
  • module – metadata representing a module; module.exports is the same object as the exports argument

TIP: Omitting the dependencies array is the signal to the loader that you are using this format. Be careful, using an empty dependencies array is not the same as omitting it.

Restless Natives

Native modules are now on the near horizon for JavaScript, but that doesn’t help us while we’re patiently waiting. Fortunately, with AMD and RequireJS, you no longer have an excuse for not writing modular JavaScript. You can start giving structure to your code today and stop contributing to the perception that JavaScript is a toy language!

Be sure to look at the RequireJS resources that you can find in Safari Books Online.

Not a subscriber? Sign up for a free trial.

Safari Books Online has the content you need

Instant Dependency Management with RequireJS How-to has a good chapter on using RequireJS and creating AMD modules.
The Twitter Flight Edge has a section on module loading with RequireJS.
Backbone.js Cookbook has a section on organizing a project structure with RequireJS.

About the author

benhockey Born and raised in Australia, Ben Hockey lives in Nashville, TN with his wife and 3 kids. As a Senior Software Developer on an amazing team of developers at SitePen, he builds Web-based Applications using The Dojo Toolkit.

Tags: AMD, Asynchronous Module Definition, CommonJS, Dependencies, Dojo, James Burke, Javascript, RequireJS,

Comments are closed.