Chapter 3. Modular JavaScript

This chapter covers

  • The definition of modules
  • Reasons to use modules in your SPA
  • A review of JavaScript scope and object creation
  • An analysis of a module’s syntax and mechanics
  • An introduction to module loading and AMD modules

JavaScript is a powerful yet extremely flexible language that has become the de facto standard for adding interactivity to web applications. Even large companies such as Amazon.com, Google, and Walmart rely on it. It’s remarkably easy to learn and even easier to add working code to a web page. So it’s no wonder that the freedom it offers and its ease of use have contributed to its popularity.

The friendliness of the language is a double-edged sword, though. In any language as dynamic as JavaScript, it can be easy to find yourself with a chaotic code base if you’re not careful. In a presentation for Google Tech Talks, renowned developer, architect, author, and speaker Douglas Crockford stated that JavaScript has “some of the best ideas ever put into a programming language” (see http://googlecode.blogspot.com/2009/03/doug-crockford-javascript-good-parts.html). He added, however, that it also has “some of the worst ideas ever put into a programming language.”

Fortunately, following best practices and proven design patterns goes a long way toward keeping your project under control. One such pattern that’s particularly useful in the development of modern web applications is the module pattern. Modules provide an elegant way to structure your code. They also help avoid many issues that would otherwise arise from having your entire application in a single page. This chapter provides an in-depth look at modules and why you should consider using them in an SPA.

3.1. What is a module?

In general, a module can be defined as a part, or component, of a larger structure. The term module, however, can have a variety of meanings depending on the context, even within the category of software development. Sometimes you’ll hear people talking about a module in a more general sense. They might say, for example, “the payments module” or “the trip planner module.” In these cases, it would be perfectly legitimate for them to be referring to the feature as a whole. When we’re specifically referring to a JavaScript code module, we mean a single function—a special function created by using the module pattern.

Basic module pattern

Don’t worry about the strange syntax at this point. Section 3.3 provides greater detail. For now, you’ll continue learning the basics of modular programming by reviewing a few concepts.

3.1.1. Module pattern concepts

Before moving forward, let’s take a moment to get acquainted with a few module pattern concepts:

  • Namespace —A namespace is a way to provide a particular scope for a group of related members. Though namespaces aren’t part of the JavaScript language currently, you can still achieve the same effect by assigning your module function to any variable with a wider scope (a global variable, for example).
  • Anonymous function expression —An expression exists if your code is part of an expression and doesn’t start with the function keyword. If the function expression being assigned is unnamed, it’s called an anonymous function expression. The module’s body is contained within an anonymous function expression.
  • Object literal —JavaScript provides a sort of shorthand for creating objects known as literal notation, which declares an object by using curly braces, with its properties defined as key-value pairs. Object literals are described in more detail in section 3.3.2.
  • Closures —Normally when a function finishes executing, the life of any local variables created within it comes to an end as well. An exception to this is a closure, which occurs when a function contains variable references outside its own scope. In JavaScript, each function has its own scope property, which references the variables of its outer scope. Every function always has an outer scope, even if that outer scope is just the global scope (so technically all functions are closures). Closures are important to our discussion because even though the module pattern’s outer function executes immediately, any objects or values up the scope chain referenced in the return statement can’t be garbage collected while the module is still in use.

3.1.2. The module’s structure

A module’s structure cleverly uses a function as a container to encapsulate its logic. This is possible because variables and functions declared locally inside the module aren’t directly accessible from outside its outer function. Access to the module’s internal functionality is regulated by what’s exposed in the return statement (see figure 3.1).

Figure 3.1. In a module, the outer function encapsulates its functionality. There’s no direct access to internal variables and functions. Access is regulated via functions in the object literal it returns.

To see how to use such an odd-looking construct, let’s take a simple function and rewrite it by using the module pattern. The following function adds two numbers and returns the result:

Listing 3.1 shows the same functionality, now written as a reusable module. Notice how the pattern has abstracted away the internal details of the module. Users of the function don’t have to worry about how the module does what it does. They know that calling the function addTwoNumbers() in the public interface will alert the correct result. Again, in section 3.3 you’ll see exactly how this is made possible.

Listing 3.1. Function to add two numbers rewritten as a module

3.1.3. The revealing module pattern

Part of the appeal of the module pattern is that a clear division is created between the internal workings of the module and its publically available functions. A heavily used variation of the module pattern that makes this division even clearer is called the revealing module pattern.

Proposed by Christian Heilmann, this slightly improved version seeks to make the public interface’s use self-evident. To do this, it moves any code needed by the API internally as well, leaving the public functions as mere pointers to the internal code. The following listing demonstrates the same module from listing 3.1, written using the revealing module pattern.

Listing 3.2. Revealing module pattern

As you can see, the public API is much cleaner and more readable. You’ll use this version of the module pattern for the examples in the rest of this chapter. Now that you have a basic understanding of a module, let’s talk about the reasons why you should consider modular programming.

3.2. Why modular programming?

The module pattern was created in 2003 and popularized by Douglas Crockford through his lectures. The structure does the following:

  • Keeps parts of our code private, only for use within the module
  • Creates a public API to regulate access to the module’s functionality

We can also assign a single namespace for the module and its related submodules to reduce the pollution of the global namespace. Why are these things important, though? Just looking at the module pattern’s syntax for the first time can be off-putting, maybe even a little intimidating. If you can get past the unusual syntax, however, there are good reasons to consider using the module pattern in an SPA.

3.2.1. Avoiding name collisions

Name collisions for variables and functions can happen, even for small applications. The likelihood magnifies exponentially the larger your application gets, because in JavaScript all your variables and functions get thrown into a global namespace. Going back to Douglas Crockford’s presentation, he says, “The worst part [of the JavaScript language is] by far—global variables.” He says this to specifically point out the problem of name collisions for globally defined code. When all your objects are in the global namespace, they all share the same scope. When you have variable or function declarations in the same scope, with the same name, no error is generated. The last declaration overrides any prior declarations. This can lead to unexpected results, which are difficult to troubleshoot. Let’s consider the following example.

Pretend you have a new startup company called The Simpler Times Gourmet and you’re creating a single-page application for it to sell your gourmet wares. (Chapter 4 covers the creation of views, so for now you’ll confine the UI to a couple of DIV tags to stay focused on the code.) You want to entice new customers to order something by offering a generous 25% discount on their first order. You’d also like to reward returning customers, but allowing everyone to take 25% off doesn’t provide the profit you’ll need to make your first year’s earnings goal. So for now, you’ll offer discounts only to new customers.

At first, everything’s going swimmingly. Your code correctly greets existing customers with a simple welcome message. Only new customers are greeted with a message about a 25% discount. I’ve hardcoded this example so it’s sure to trigger the message for returning customers. Figure 3.2 shows the output in the browser.

Figure 3.2. Returning customers correctly greeted with a simple message and no mention of a discount

Listings 3.2 and 3.3 show the working code. This design doesn’t use modules. When the DOM is ready, you call getWelcomeMessage() (defined in welcom-Message.js) to return the appropriate greeting. The greeting is then inserted into the content DIV via jQuery’s html() function so the message can greet arriving customers, as shown in the following listing.

Listing 3.3. index.html

Pay particular attention to the way the result of the getStatus() function in listing 3.4 is used. Whether the correct message displays depends on whether getStatus() returns existing or not.

Note

The jQuery .ready() function guarantees that the DOM will be ready before the function defined inside executes.

Listing 3.4. welcomeMessage.js

Everything works as expected until an enhancement is added to the application. It seems pretty benign, just an additional JavaScript file with code to display a message about the current status of the shopping cart. But after adding the code, functional tests fail. The cart status message is correct, but returning customers suddenly get an erroneous message offering them the 25% discount also. Now everyone, regardless of customer status, is offered the new customer discount. Figure 3.3 shows the welcome screen after the new file is added.

Figure 3.3. Suddenly everyone’s being told they’re in for a big discount!

When the new file was unit tested in isolation, it seemed to work fine. Why did it cause issues when added to the site? As you’ll see in listing 3.5, a name conflict is to blame. The developer who created the new file inadvertently included a function called getStatus(). Because both the original getStatus() and the new get-Status() are defined in the global namespace, a name conflict arises.

Listing 3.5. shoppingCartStatus.js

Unfortunately, you get no error messages in the browser. Because the new file was included last (see listing 3.6), the new getStatus() function merely overrides the first one (see figure 3.4). It’s as if the first one never existed.

Figure 3.4. Without a module to limit the scope of each function, both are added to the same global scope, causing a name conflict.

Now the wrong getStatus() function is used by the getWelcomeMessage() function to create the greeting. What’s interesting about this example is that the getStatus() function in the new file has nothing whatsoever to do with the welcome message, yet it still has a profound effect on the result.

Listing 3.6. index.html

Now let’s see if we can fix this mistake. Let’s try recoding the application’s logic by using modules. I’ve tried to keep the logic of each function as close to the original as possible, so it’s easier to follow along. The following listing shows your new index page.

Listing 3.7. index.html

You’ll immediately notice a few changes, including the addition of a couple of new JavaScript modules. First, your call to jQuery to update the DIV content has been replaced with a call to your first module. The application now starts with a single call to the STGourmet module’s only public function, init().

Second, you now have a total of four includes, not counting jQuery. The existing JavaScript files have been recoded in module format but kept in their original include files. You also have a couple of new modules. Pay attention to the load order. If you’re using standard SCRIPT tags to load the files, modules must be ordered so that any variables or functions needed later are already loaded when needed. STGourmet is your main module, so it must be first in the list. Customer has functions that are used later, so it will come second.

Chapter 9 presents ways to combine and minify your code for better performance. Section 3.4 also covers using a third-party library called RequireJS to load modules asynchronously. For now, you’ll leave them as normal file includes, which get loaded synchronously. Therefore, load order is important.

Note

When modules are included using the SCRIPT tag, load order is important! Modules should be loaded in the order in which they’re needed.

Listing 3.8 shows your first module. In it, you define STGourmet as the single global variable in the whole application. This carves out a single namespace where your entire application will live. Again, don’t worry about the syntax for the module pattern itself right now. Section 3.3 covers that thoroughly. For now, let’s continue to focus on how the modules are used to solve the name conflict problems.

Listing 3.8. STGourmet.js

Because only functions declared in the return statement are public, this module’s init() is its only public function. In index.html, we started the application by calling STGourmet.init(). This, in turn, calls the displayStatus() function of the shopping-Cart submodule and the showGreeting() function of the welcomeMessage submodule.

The following listing contains your customer module. Because our three private hardcoded variables aren’t part of the shopping cart or the greeting, this code was pulled out into a module of its own.

Listing 3.9. customer.js

Submodules

One unique thing you did in this file was to create a submodule. It’s a common practice when creating an application using the module pattern to create submodules. This allows you to break the code into separate modules, yet still have it be part of the same namespace.

You’ll notice you didn’t start the submodule declaration with var. This was no accident. In reality, you’re using dot notation to add a new property to the previously defined function object called STGourmet. By adding .customer to your STGourmet object, you create a submodule named customer (see figure 3.5).

Figure 3.5. You use dot notation, without a var, to declare a submodule. What you’re really doing is adding a property called customer, which itself contains a module.

The following listing contains your welcomeMessage submodule. As you can see, it contains the same logic as before, only wrapped in the module pattern.

Listing 3.10. welcomeMessage.js

Finally, the following listing contains your shoppingCart submodule.

Listing 3.11. shoppingCartStatus.js

Now that you have all of your code in modules, avoiding any name conflicts, let’s see what the application looks like (see figure 3.6).

Figure 3.6. Using modules, our welcome message is once again correct!

Using the module pattern, you can easily write code without fear of name conflicts. Now both getStatus() functions can coexist, with each functioning as expected (see figure 3.7).

Figure 3.7. Now that each getStatus() function lives in its own module, there are no more name conflicts.

As you’ve seen, the module pattern gives you the freedom to name your variables and functions any way you want, without worrying about name conflicts from code inside other modules. This small detail becomes crucial as your project grows in size and complexity.

3.2.2. Protecting your code’s integrity

In some languages, access to certain parts of an application’s code can be controlled through access-level modifiers, such as public or private. In JavaScript, the keyword private is reserved but not available as of this writing. So you can’t explicitly declare an attribute of an object to be private. But as you’ve already seen, you can still limit the accessibility of variables and functions with the module pattern. This is possible because variables and functions declared inside a function are private in the sense that they’re scoped to the containing function. The ability to restrict access to certain parts of a module’s code prevents other code from directly changing its internal state. This keeps the internals of the module working as expected and its data from being set to something that’s invalid for the module’s intended purpose.

Let’s illustrate with a basic counter. A simple function to increment a value every time it’s invoked is pretty innocuous:

The code as it’s currently written works correctly. But there’s nothing to prevent the author of displayNewCount() from updating the count variable directly, even though it’s vital for incrementCount() to work properly. Let’s imagine the programmer wants to change displayNewCount() so it prints the word time at the end if the count is 1; otherwise, it ends the sentence with the word times. Let’s see what happens if the function is allowed to update the count variable itself:

By being allowed to directly manipulate the data used by the incrementCount() function, the printCount() function causes the value of the count variable to be NaN (not a number), which is invalid for our business logic.

Now let’s rewrite the code by using the module pattern. In the next listing, we’re providing only the consumer of incrementCount() access to the current count via its public interface. This effectively blocks the direct manipulation of the variable.

Listing 3.12. Code rewritten using a modular design

Bulletproofing code that will be used correctly is difficult enough. This task becomes almost impossible if you can’t prevent the internal workings of your code from being misused. The module pattern offers a way to manage access to internal code.

3.2.3. Hiding complexity

When we talk about hiding complexity in programming, it’s not because we’re trying to keep secrets. We’re also not talking about security. We’re referring to the difference between having complicated logic for a particular feature strewn across a multitude of global functions versus internalizing the complex logic and putting only what other developers need to use in a public interface. It reduces clutter and makes it clear which functions to call in order to correctly use the functionality. Figure 3.8 illustrates how a module with a public API can make it easier for other developers to know how to use your code.

Figure 3.8. The module pattern makes it clear how others should use your code.

By hiding the complexity of your module, you’re enabling others on your team to use it without having to understand all the nuts and bolts of its internal code.

3.2.4. Reducing the impact of code changes

Internalizing how your code works also means that other programmers will be coding to only your public interface. As long as the behavior is the same when using the module’s API, the internal code can change without forcing other parts of the application to change in turn. Reducing the amount of change to other parts of the system reduces the amount of effort and time that goes into code maintenance.

You, as the programmer of the module, can more freely make changes to your code. If the contract of your API remains intact, the chance of inadvertently introducing unrelated code bugs is greatly lessened.

3.2.5. Organizing your code

Staying organized makes our lives easier and more efficient. This is true in programming as well. Unless you’re using the ECMAScript 6 version of JavaScript, you can’t formally declare your code as part of a class or module or subroutine. Nevertheless, the ability to define a group of variables and functions as part of a unit of functionality is a good practice.

The module pattern gives you a way to take your code out of the global namespace and organize it in a more meaningful way. You can start to think of the code in terms of its overall functionality rather than individual functions (see figure 3.9).

Figure 3.9. The module pattern helps organize code into units of functionality rather than individual functions.

Ultimately, refactoring your code into well-organized units will lead to greater efficiencies in terms of reuse, maintenance, and future updates.

3.2.6. Understanding disadvantages of the module pattern

Unfortunately, there’s no silver bullet when creating complex code. This is especially true of modern web applications. For all the benefits offered by the module pattern, you should be aware of some of its disadvantages:

  • When testing —Some people don’t like the inability to unit test private functions inside the module. Others, however, advocate that unit-testing functionality via the module’s public API is a more valid way of testing. Therein lies the controversy. Even though the inability to test private code is seen as a disadvantage by some, others feel that if you have to test private functions, then you should consider whether they should be made public. Their argument is that unit tests should be designed to test the interfaces to an object, not the private code within the object.
  • When extending objects —JavaScript programmers are used to being able to extend any object, at any time, by merely adding to it. This is a testament to the powerful yet flexible nature of the language. But as you’ve learned, objects that are defined within the module and aren’t part of the public API are unreachable outside the module. Therefore, adding a new property or method to a private object inside the module won’t work. Then again, seeing this as a disadvantage is all in how you look at it. As you saw previously, this can also protect the integrity of your module’s core functionality.

3.3. The module pattern dissected

So far you’ve learned a great deal about the module pattern and how to use it. What you haven’t done is take a deep dive into its structure. Understanding why its syntax is the way it is will help you feel more comfortable when incorporating the module pattern in your single-page application. In this section, you’ll dissect the module pattern to see how it does what it does.

Let’s start by revisiting the original boilerplate structure to have it fresh in your mind:

var moduleName = (function() {
    return {
    };
})();

This formula is the skeleton for the basic module pattern.

3.3.1. Achieving privacy

JavaScript has only two types of scope: local and global. Declarations made inside a function are local (private), and those made outside any function are global (public). Because you can’t explicitly mark variables and functions as public or private, you’re left with only their scope to work with. That narrows things down a great deal.

The only way to achieve privacy in JavaScript is to make your declarations locally within a function. Figure 3.10 highlights the anonymous function of the pattern, which internalizes the module’s functionality.

Figure 3.10. The outer function of the pattern creates a local scope for variables and functions. This gives the module a way to achieve privacy for internal code.

As you may recall from our discussion of the module pattern in the previous section, this ability to internalize code is essential to achieve its benefits, such as avoiding name conflicts, protecting your code’s integrity, and providing managed access to the module’s functionality.

3.3.2. Creating the public API

You create the public API of a module by combining a few techniques. These techniques make for unusual syntax but work perfectly together to give the desired effect.

Return an object literal

Instead of a simple value, such as true or 3, an object is returned in your module pattern (see figure 3.11).

Figure 3.11. An object literal is returned. Its functions have access to the module’s internal variables and functions. This gives calling code regulated access to the module’s functionality.

The returned object can have any assortment of variables and functions, but functions are what allow you to expose behavior. That’s why you typically see only functions available in the returned object.

The object literal is favored for the returned object, because its syntax provides a nice facility to define an object in a single, hierarchical fashion without the need for the new keyword. As you saw earlier in the revealing module pattern, the exposed functions are merely pointers to the inner functions.

Refresher on object literals

As a refresher, let’s look at object literals. In JavaScript, you can create objects with the new keyword, with the Object.create() function (ECMAScript 5), or with literal notation (also referred to as an object initializer). With literal notation, the object is defined using curly braces. Its properties and values are in the form of name-value pairs, separated with a colon. You’ll also need to put a comma after each pair except the last one. Values can contain variables, functions, or other objects. Here’s an example:

Cause the function to return immediately

Normally when a function is declared, it doesn’t return until it’s invoked by other code calling it. In the case of the module pattern, you need the variable you’re using for the module’s namespace to point to the returned object literal, not the function itself. This is accomplished by adding the trailing set of parentheses to the structure (see figure 3.12).

Figure 3.12. The trailing parentheses cause the anonymous function of the module pattern to be invoked immediately, returning the object literal.

Without the trailing parentheses, the variable will be assigned the entire function, not the object being returned. With them, however, the anonymous function is invoked immediately, returning the object literal to the assigned variable. This is referred to as an immediately invoked function expression, or IIFE.

A closure is formed

In order for this entire collection of techniques that are used to form this pattern to work, any private variables or functions referenced/in scope by the returned object literal can’t be garbage collected. If they were, you’d get errors when trying to use the API. But a special situation occurs when your object literal is assigned to the moduleName variable. The object literal functions are now available for use, so they can’t be garbage collected. Because these functions in the object literal also have references to internal private objects, those objects can’t be garbage collected either. As you learned in our definition of a closure, this is possible because all functions have a scope that references an outer lexical scope. It’s the closure that keeps the internal functionality alive long after the IIFE has finished executing (see figure 3.13).

Figure 3.13. A closure keeps any variables or functions referenced in the IIFE alive, even after execution.

In holding onto their references, the module’s inner functions can continue to safely operate without becoming undefined when the outer function finishes executing.

3.3.3. Allowing for global imports

The trailing parentheses also give you a way to declare items you want passed into the anonymous function via its parameters. In module terms, these external objects you’re bringing inside for internal use are called imports.

Using this facility to import global variables into the module is a common practice. It not only makes it clearer to someone reading the code what’s being used but also helps speed up the variable resolution process for the interpreter. Finally, it allows you to alias a global variable, if desired, within the scope of the function. Take, for example, jQuery:

The $ is the way most of us like to reference jQuery. It’s much easier than typing out jQuery all the time. But many libraries out there also want to use $ in their code. Because you’re specifically aliasing jQuery locally within this module, there’s no chance for the $ in this instance to conflict with the $ from another library.

3.3.4. Creating the module’s namespace

The final part of the module pattern is the establishment of its namespace. This namespace gives you a way to call the module’s public API, as well as somewhere to assign any submodules that may be desired.

In JavaScript, a function can be declared or assigned as an expression. You just read that the outer function of the module pattern is an IIFE. The assignment of the immediately invoked anonymous function to a variable not only gives you a pointer to the returned object literal but also creates the module’s name. It also defines the module’s namespace if submodules are attached (see figure 3.14).

Figure 3.14. This assignment creates the module’s namespace.

Tip

The parentheses around the IIFE aren’t required because this is a function expression. This visually establishes a boundary for the module (see figure 3.15).

Figure 3.15. The outer parentheses aren’t required.

And with that final note, your dissection of the module pattern is complete. As you can see, each part of the pattern has a purpose. The end result from incorporating this pattern is that your code base can remain clean, purposeful, and able to grow with your project.

3.4. Module loading and dependency management

In most browsers, the SCRIPT tag used in your module’s source file creates a blocking condition. The application pauses while scripts are loading. So the more module files you have, the more lag your users will experience while waiting for the application to load. A deluge of HTTP requests can also tax your network.

To help alleviate this issue, you can concatenate the modules into as few files as possible and optimize the final files. Chapter 9 covers tools for these two techniques. But in spite of the gains from using these two techniques, you still have the SCRIPT tag’s synchronous nature to contend with. To tackle this problem, you can also look at libraries that load your modules asynchronously.

3.4.1. Script loaders

Being able to bypass the blocking condition of the SCRIPT tag gives a tremendous boost to your application’s load time. HTML introduced native nonblocking support for loading and executing JavaScript code through its defer and async attributes for the SCRIPT tag. The defer attribute specifies that a script is executed only after the page has finished parsing. The async attribute, on the other hand, asynchronously executes the script as soon as it’s available. When using the SCRIPT tag, it’s up to you to make sure scripts are correctly ordered so that dependencies are available when needed. An alternative approach is to use AMD script-loading libraries.

AMD script loaders handle the low-level, boilerplate code to manage the asynchronous download process. They also allow you to specify dependencies that must be present for a module to function. If the dependent modules aren’t there, the framework will go get them for you and make sure they’re downloaded and ready before proceeding. You have a multitude of script loaders to choose from—LABjs, HeadJS, curl.js, and RequireJS, to name a few. Each one is slightly different but serves to tackle loading and management issues. It’s also worth mentioning that, though not ratified as of this writing, the ability to asynchronously load scripts is a proposal for CommonJS-style script loaders.

Nothing is ever perfect, though. Asynchronously loading scripts speeds things up but introduces another problem: unpredictable resource availability. When scripts are loaded asynchronously, there’s no way to precisely know which one will be first. It’s entirely possible for a file to download and start executing before all necessary dependencies are in place. Creating the fastest load time possible is for naught if your scripts fail because their dependencies aren’t yet loaded. Good news, though. Most script-loading libraries take care of this issue as well.

Script loaders defer script execution until the file and any required dependencies needed by the module are loaded. Most cache the module as well, so it’s loaded only once, no matter how many times it’s requested.

To illustrate some basic loading and management concepts, I have to choose a library to use. We’ll use RequireJS because it’s currently a heavily used script-loading library. RequireJS will also give you a chance to become acquainted with a popular module format that differs slightly from the traditional module pattern you’ve already seen. It’s called the Asynchronous Module Definition (AMD) API. The next sections define AMD and walk you through script-loading basics with RequireJS.

3.4.2. Asynchronous Module Definition

AMD started as the draft of the module format for a larger project called CommonJS. CommonJS was an attempt to not only solve the issue of a missing standard module definition in the JavaScript language but also make this single format work for a variety of environments, including the server. But among the CommonJS group, full consensus wasn’t reached on the module specification (as of this writing), and AMD was moved to its own group, the AMD JS group (https://github.com/amdjs).

AMD has largely been adopted for use within web browsers. This specification defines not only a standard module format but also how to asynchronously load it and its dependencies. The specification defines two structures: define and require. I’ll talk about both before diving into an example using RequireJS.

define

Use define to declare a module, as you did using the module pattern previously. Like the module pattern, the AMD module allows for imports, which can be accessed via matching function parameters. The body of the AMD module is also nested inside an outer containing function, just like the module pattern. Another similarity is that the AMD module’s body contains private functionality that can be exposed via its return statement.

At this point, you’re probably asking yourself why AMD exists, if you already had a perfectly good way to create modules using the module pattern. The main reason is that AMD represents a formal specification for defining modules, absent having one in the language. But more specifically, it’s a specification for defining modules and their dependencies that are to be loaded asynchronously.

AMD—define

Tip

Even though the specification allows for a module ID, it’s normally omitted. If the ID is left out, it’s generated internally by the script loader. In turn, the module is managed internally via the generated ID. Unnamed AMD modules are more portable, allowing you to freely move the module to new directories without code updates.

Notice that a namespace isn’t defined as with the module pattern. Another one of the perks of using AMD and script loaders is that namespaces are no longer needed. Modules are managed by the library. You also don’t have to worry about the order of the dependencies. They’ll be loaded and available by the time the module’s function executes.

require

The require syntax is used to asynchronously load/fetch a particular module. In this structure, you define the modules to be loaded in the same way that dependencies are declared using the define syntax. A callback function is executed when the required modules are fetched and ready.

AMD—require

The require structure is a directive, not a module declaration. So whereas module definitions are usually one per physical file, require should be used anytime you need it. It can be used by itself or from within a module.

3.4.3. Using AMD modules with RequireJS

Quite often concepts are easier to understand after seeing them in action. So let’s take one of the earlier examples and convert the modules into AMD-style modules. You’ll use RequireJS as the module-loading library to implement AMD.

Before you begin the example, let’s go over some RequireJS concepts. Even though you’ll focus on module loader concepts, you still have to get some of the RequireJS basics under your belt to be able to complete the example:

  • data-main —Everything has to start somewhere. Just as a basic Java or C# application has a “main” method, RequireJS similarly has a point of origin. When you add a SCRIPT tag to your SPA to include the RequireJS library, you’ll add a data-main attribute to it. This is the starting point of a RequireJS-loaded application. Normally your main JavaScript file will contain the RequireJS configuration and a require directive to execute the initial AMD module.
  • requirejs.config() —This function establishes the RequireJS configuration options. Its only input is an object literal that will contain all configuration properties and their values.
  • baseUrl —This is a path relative to your web application’s root directory. Any other paths in the configuration object will be relative, in turn, to this path.
  • path —You have to tell RequireJS where to find your modules. A path maps a module name (which you invent) to a path on the web server, relative to the baseURL (and the application’s root directory). It can be the full path to the module’s source file (minus the extension) or a partial path. If a partial directory path is used, the rest of the path to the file must be included anywhere the object is listed as a dependency.

You have many more configuration options, but we’ll stick to the basics to keep the emphasis on learning AMD, not RequireJS specifically. If you want to learn more about RequireJS-specific options, the documentation can be found at http://requirejs.org.

Now that you’ve been introduced to a few basic concepts, let’s try converting an example’s modules to AMD modules. You’ll use the example from section 3.2.2, because it’s a small example and will be easy to compare against the original version.

First, you’ll need to download the RequireJS library from http://requirejs.org. Figure 3.16 has the correct download option circled.

Figure 3.16. RequireJS has no dependencies to download. You need only the require.js file.

With the necessary files in place, the example’s directory now looks like the following listing.

Listing 3.13. AMD example directory structure

When you open your default URL in the web browser, you’ll immediately get the index.html page, because the server has been configured to use it as your welcome page. All you need to do here is add a single JavaScript include to point to the RequireJS library. Inside the SCRIPT tag, you’ll use the data-main attribute to let RequireJS know that your main file is called main.js and can be found at the web application’s root (see the following listing).

Listing 3.14. index.html

Next, you’ll need to add some basic configuration to main.js. This example is simple, so you need to configure only your baseUrl and path properties so RequireJS can find the modules. You’ll additionally use the require directive to call displayNewCount() from your displayUtil module twice. Calling the function more than once will let you know that the counter is incremented appropriately (see the following listing).

Listing 3.15. main.js

Looking at the code for the require call from listing 3.15, notice that you told RequireJS you wanted to use the displayUtil module in the callback function by including its module name from the paths section. Each module-name string in the dependency list should have a matching function parameter. Inside the callback function, you use this parameter to reference the module.

The following listing shows the module for the display utility. Because this is a module declaration, you’ll use the define syntax.

Listing 3.16. displayUtil.js

In the displayUtil module, your only dependency is the counter module. The body of the module is nearly a one-to-one match with the original, non-AMD version. Keep in mind that you don’t need to assign the module to a namespace, because modules are managed internally by RequireJS.

Finally, the next listing shows the counter module. Because it has no dependencies, you can leave out that part of the structure. With no dependencies, your outer function will have no parameters.

Listing 3.17. counter.js

With all of the files in place, you can start your server and navigate to the default URL. The application will start after the require directive is reached. Figure 3.17 shows the network console from the browser. Now you can see the module loader in action.

Figure 3.17. The module loader correctly downloads and manages our AMD modules and dependencies. The require directive in main.js declares a dependency that instructs RequireJS to fetch the displayUtil module. The displayUtil module, in turn, has a dependency on the counter module that gets dynamically loaded by the module loader.

From the output in the network console, you can see that all required modules were downloaded automatically by the module loader. All you had to do was to add a dependency to your require or define declaration. Because each dependency was passed into the module via its corresponding function parameter, you had access to the dependent module’s public API. Additionally, because RequireJS manages the modules for you, you didn’t need any namespaces. This is a testimony to the power of using AMD modules and the module loaders that implement this module specification.

3.5. Chapter challenge

Now here’s a challenge for you, to see what you’ve learned in this chapter. In browsers such as Firefox or Chrome, you can add styles to messages printed to the browser’s console by using %c, as in this example:

console.log("%c" + errorMessage, "color: red");

See if you can turn this into a reusable logging module. Expose functions that will print different colors based on these logging modes: debug, info, warning, and error. Internally create variables representing each mode’s color: black for debug, green for info, orange for warning, and red for error. You’ll also need to define internal functionality that prints in a color that matches the logging mode used.

3.6. Summary

This chapter described what a module is and listed what the module pattern does for you:

  • Keeps parts of your code private, only for use within the module
  • Provides for a public API, which hides complexity and protects the integrity of a component’s code by providing regulated access to the internal code
  • Prevents name collisions that occur when everything’s defined in the global namespace
  • Reduces the impact to the project when code changes
  • Provides a way to divide areas of concern in your application into manageable, more meaningful units

Nothing’s perfect. This chapter also presented some disadvantages of the module pattern:

  • Unit testing is limited to a public API.
  • Internal objects can’t easily be extended.

Finally, you looked at a more formal specification for modules called Asynchronous Module Definition (AMD) and learned how module loaders eliminate the need for namespaces and asynchronously fetch your modules and their dependencies.

In the next chapter, you’ll look at another way to separate areas of concern: using MV* libraries to separate your JavaScript code from your HTML.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset