Book Image

Learning Underscore.js

By : Alexandru Vasile Pop
Book Image

Learning Underscore.js

By: Alexandru Vasile Pop

Overview of this book

Table of Contents (14 chapters)
Learning Underscore.js
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

Useful patterns and practices for JavaScript applications targeting ECMAScript 5


The examples we have used so far had an introductory role and they are not representative of a production ready application. One of the goals of this book is to present examples that are closer to real life usage and the rest of the examples will follow this goal. As a first step, we need to introduce some JavaScript patterns and practices that will be useful in organizing and driving the rest of the examples in this book. They are essential when writing ES5 code, but you don't need them when writing ES6 code using classes and modules (more details about ES6 and how it improves code quality will be discussed in Chapter 6, Related Underscore.js Libraries and ECMAScript Standards).

One of the difficult problems to solve when writing JavaScript code is to avoid the pollution of the global scope. Any variable declared outside of a function body will automatically be visible in the global scope. You can easily imagine a scenario where your variable names clash with the variables defined in other JavaScript files or libraries. Also, JavaScript automatically moves all variable declarations to the top of the current scope. This behavior is called hoisting and can lead to scenarios where you use a variable before it is declared, which is confusing and can cause unintended errors.

To avoid these problems, a typical workaround is to use a function body to declare your variables. Variables declared in this way belong to the local scope of the enclosing function, and they are invisible to the global scope. This workaround is based on two patterns used frequently in JavaScript applications: the immediately invoked function expression (IIFE)—pronounced "iffy"—and the revealing module pattern.

The immediately-invoked function expression

If we append the line, console.log(people.length);, at the end of the script section from the previous example found in the underscore.map.reduce folder, we will see the length of the array written in the console as 6. If we convert the example to use an immediately invoked function expression, the script section will look like the following code (with most of the code removed for brevity):

(function(){
  var people = […];
  $(document).ready(function() {…});
}());
console.log(people.length);

You can view the preceding example either online at http://bit.ly/1WNL31t or in the underscore.map.reduce-iife folder from the source code for this chapter.

I have highlighted the changes required to convert the code to use the immediately invoked function expression. Sometimes, a leading semicolon is used to prevent issues caused by the automatic semicolon insertions in JavaScript when your scripts get concatenated with other scripts. The enclosing parentheses around the highlighted self-executing anonymous function and before the last semicolon are the signature for these types of expression.

The example will still work as before, but the console output will have this message: Uncaught ReferenceError: people is not defined. Using this pattern, we made the people variable inaccessible for the global scope, while leaving it available for the IIFE function scope. The Underscore library is using an IIFE that contains all its source code and we will follow suit by ensuring the rest of the examples will use this pattern whenever applicable.

Next, we will explore a JavaScript pattern that also prevents exposing data to the global scope and is useful for safely sharing application code between different components in JavaScript.

The revealing module pattern

The revealing module pattern solves the problem of hiding implementation details for JavaScript objects that expose property-like objects or functions to the global scope. The following example is a plain JavaScript one—no external library references are required:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>Getting started - Revealing module pattern</title>
</head>
<body>
  <h1>See browser console for example output.</h1>
  <script>
    var revealingModule = (function() {
      var innerObject = 5;
      var innerFunction = function(value) {
        return innerObject + value;
      };
      return {
        outerObject1: innerFunction(1),
        outerObject2: innerFunction(2),
        outerFunction: innerFunction
      };
    }());
    console.log("outerObject1:" + revealingModule.outerObject1);
    console.log("outerObject2:" + revealingModule.outerObject2);
    console.log("innerObject:" + revealingModule.innerObject);
    console.log("outerFunction(3):" + revealingModule.outerFunction(3));
    console.log("innerFunction(3):" + revealingModule.innerFunction(3));
  </script>
</body>
</html>

You can view the example either online at http://bit.ly/1hUg2J5 or in the revealing-module-pattern folder from the source code for this chapter.

The first highlighted code represents the object created through the revealing module pattern, and you will notice that it relies on an IIFE to define itself. Using this pattern, all of the variables declared inside the IIFE are inaccessible to the outside scope, and the only visible properties are the ones returned in the object created in the last highlighted code snippet. The console output for this example is shown as follows:

outerObject1:6
outerObject2:7
innerObject:undefined
outerFunction(3):8
Uncaught TypeError: undefined is not a function

Any reference to the variables defined within the IIFE will be unsuccessful as demonstrated by the exception raised when the innerFunction was called within the global scope. You will see this pattern in action throughout this book, as we will use it in many examples. The Underscore library is using a version of this pattern and exposes all its global accessible functions though its _ object. The annotated Underscore source code available at http://underscorejs.org/docs/underscore.html is great for exploring how the library works behind the covers and is the recommended companion for this book.

Note

You can find more information about the revealing module pattern and other JavaScript design patterns in the online resource Learning JavaScript Design Patterns, Addy Osmani, O'Reilly Media, available at http://addyosmani.com/resources/essentialjsdesignpatterns/book/.

Next, we will convert the underscore.map.reduce-iife example code to use the revealing module pattern and define a global accessible object called awardAgeCalculator. This object exposes all relevant functions that need to be accessible outside the IIFE scope as shown in the next code snippet:

var awardAgeCalculator = (function() {
  var getPeople = function() {
    return [{
      name: "Herta Muller",
      birthYear: 1953,
      awardYear: 2009
    }, {
       ...
    }, {
      name: "Patrick Modiano",
      birthYear: 1945,
      awardYear: 2014
    }];
  };

  var innerGetPeopleWithAwardAge = function() {
    return _.map(getPeople(), function(person) {
      return {
        name: person.name,
        awardAge: person.awardYear - person.birthYear
      };
    });
  };

  return {
    getPeopleWithAwardAge: innerGetPeopleWithAwardAge,
    getAverageAwardAge: function() {
      var peopleWithAwardAge = innerGetPeopleWithAwardAge();
      var totalAwardAge = _.reduce(peopleWithAwardAge, function(memo, person) {
        return memo + person.awardAge;
      }, 0);
      return totalAwardAge / peopleWithAwardAge.length;
    }
  };
}());

You can find the example in the underscore.map.reduce-revealing-module folder from the source code for this chapter or you can execute it online at http://bit.ly/1h7eg6x.

I have highlighted the two functions that are defined in the local IIFE scope and cannot be accessed directly unless they are referenced as properties in the object returned by the awardAgeCalculator IIFE. Notice the functional style used in the example; apart from the awardAgeCalculator object, we use only functions throughout and don't have any variable inside awardAgeCalculator that preserves its value between any two function calls. We will continue to use this approach to define the core functionality of an example and here is how awardAgeCalculator is used to generate the example output as part of the same example:

$(document).ready(function() {
  var outputContent = "<br />Award age for people:";
  _.each(awardAgeCalculator.getPeopleWithAwardAge(), function(person) {
    outputContent += "<br />";
    outputContent += " - " + person.name + " was " + person.awardAge + " years old";
  });
  var averageAwardAge = Math.floor(awardAgeCalculator.getAverageAwardAge());
  outputContent += "<br /><br />" + "Average award age is " + averageAwardAge + " years old.";
  $("#output").html(outputContent);
});

I have highlighted the usages of the awardAgeCalculator functions, and we obtained a better separation of the code and simplified the example output section.

Note

We can encounter an issue if we reference other JavaScript files that might also declare an awardAgeCalculator variable. Choosing the name of your global accessible objects is very important because of this issue, and it is a very difficult problem to solve without a dependency management utility. We will discuss how we can avoid this issue and other ways to organize JavaScript code in Chapter 6, Related Underscore.js Libraries and ECMAScript Standards.

The JavaScript strict mode

ES5 has introduced a new way to use a stricter variant of JavaScript called strict mode. This variant changes the behavior of the JavaScript runtime, and the following are some changes that occur in strict mode:

  • Some silent errors are thrown instead of being ignored, such as assignment to a nonwritable property.

  • All global variables need to be explicitly declared. When you mistype a global variable name, an exception is thrown.

  • All of the property names of an object need to be unique, and all of the parameter names for a function also need to be unique.

By including the line "use strict"; in your scripts, you can adhere to this JavaScript variant when using a modern ES5-compliant browser. If the script is loaded in an older browser, the statement is ignored and the JavaScript is parsed in non-strict mode. Strict mode can only be safely declared at the top of a function body. If it is declared in the global scope, it can cause issues when a strict mode script is concatenated with other non-strict scripts.

Using strict mode leads to safer, cleaner code with fewer errors. All examples throughout the rest of the book will use the patterns and practices discussed in this section.