Home Web Development Learning Underscore.js

Learning Underscore.js

By Alexandru Vasile Pop
books-svg-icon Book
eBook $39.99 $27.98
Print $48.99
Subscription $15.99 $10 p/m for three months
$10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
BUY NOW $10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
eBook $39.99 $27.98
Print $48.99
Subscription $15.99 $10 p/m for three months
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
  1. Free Chapter
    Getting Started with Underscore.js
About this book
Publication date:
October 2015
Publisher
Packt
Pages
224
ISBN
9781784393816

 

Chapter 1. Getting Started with Underscore.js

This chapter introduces you to Underscore and explains the main problems addressed by this library together with a quick introduction to functional programming. The chapter describes some of the concepts and patterns that are used by Underscore or are helpful when using Underscore. The final part is a walkthrough to set up the development environment used throughout the book.

The topics covered in this chapter are as follows:

  • Why Underscore

  • Getting started with Underscore by example

  • Key Underscore functions

  • Functional programming fundamentals

  • Useful patterns and practices for JavaScript applications targeting ECMAScript 5

  • Setting up a development workflow to explore Underscore

  • Testing JavaScript code with Jasmine

The chapter assumes that you know JavaScript programming fundamentals and how to create basic web pages using HTML, CSS, and jQuery.

The source code for all the examples from this chapter is also hosted online on GitHub at https://github.com/popalexandruvasile/underscorejs-examples/tree/master/getting-started.

 

Why Underscore


In the last couple of years, the JavaScript programming language has extended its reach dramatically. While initially it was a browser-based scripting language, it is now used in server-side applications via platforms such as Node.js or in mobile and desktop applications via frameworks such as PhoneGap and Node-Webkit. Database engines such as MongoDB and PostgreSQL also use JavaScript, and this makes it possible to write an application using the same programming language throughout all its layers and components. Another significant change that raised the importance of JavaScript was the emergence of libraries and frameworks that targeted the browser as an application platform. Libraries such as jQuery enabled cross-browser HTML element manipulation, among other features, and frameworks such as Backbone.js facilitated building single page applications.

Note

A single page application (also known as SPA) has the user interface rendering and navigation happening in the browser rather than on the server.

The library presented in this book is called Underscore and provides an extensive set of functions that simplify and enhance handling of JavaScript objects, arrays, and functions. It accomplishes this by providing missing functional programming features to JavaScript. By using Underscore JavaScript gains a better level of usability that makes coding easier and more expressive on a similar level to other general purpose programming languages.

Version 1.0 of Underscore was launched in 2010 around the time when single page applications started to gain more ground. Underscore is used in Backbone.js as both have the same author, and after its launch it has become one of the most popular JavaScript libraries. At the time of writing this book, Underscore has reached version 1.8.3, which will be the version used throughout all examples.

Underscore is representative of a good JavaScript utility library as it provides solutions for a specific problem domain rather than being a catchall utility. It also has very good online documentation (including annotated source code) that is available at http://underscorejs.org/, which is another distinctive attribute of a good software library. This book will explore Underscore by presenting a series of more involved examples than the ones provided with its library documentation.

To understand why Underscore is so popular, we need to discuss the ECMAScript 5 (ES5) specification and what it meant for JavaScript as a programming language. Technically, JavaScript is a specific implementation of an open language specification called ECMAScript. The current version of this specification was finalized at the end of 2009 and is known as ECMAScript 5 (or ECMAScript 5.1 to be very specific). This version added functionality for the built-in JavaScript objects, Array and Object, included new functional features, and improved the meta-programming story among other changes. Soon after its release, it started to be adopted by all major browsers, including Internet Explorer from version 9. There was still a large number of users relying on browsers such as Internet Explorer 6, 7, and 8 that were unlikely to upgrade too quickly to Internet Explorer 9 compared to users of browsers such as Mozilla Firefox and Google Chrome that had faster release and upgrade cycles. As Underscore provided support for some of the functionality introduced by ECMAScript 5, this made it a useful library for web applications targeting older browsers such as Internet Explorer 8, and for developers that wanted to write code that was based on ES5 without worrying about browser support. Although ES5 support is important, this is just a small feature of Underscore compared to the rest of its features.

All of this book's examples assume that they are executed against a JavaScript version that is ES5 compliant. All the examples can be executed on Windows, Mac OS X, and Linux, and we will mainly target Google Chrome and Mozilla Firefox that are the most popular cross platform browsers (although you should not have issues running the examples in other browsers).

Note

Technically, Underscore is not directly compatible with ES5 starting from version 1.7.0, and we will discuss more about standards compliance in Chapter 6, Related Underscore.js Libraries and ECMAScript Standards.

ES 5 support for older browsers should be provided through a library targeting this feature exclusively such as es5-shim. This library is available at https://github.com/es-shims/es5-shim, where you can find more details about its features.

 

Getting started with Underscore by example


The best way to introduce Underscore is with examples. We will target the same problem and solve it first using plain JavaScript that is ES5 compliant followed by a couple of Underscore-based solutions.

The ECMAScript 5 starter example

The examples presented in this chapter are hosted in web pages, and they can be executed by opening the index.html example file in your preferred web browser.

We start with a dataset that contains various bicycles and each one has a specific type. We want to process the dataset and get a count of bicycles for each bicycle type. The dataset is represented by an array of bicycle objects created using the object literal notation:

var bicycles = [{
      name: "A fast bike",
      type: "Road Bike"
    }, {
      name: "An even faster bike",
      type: "Road Bike"
    }, {
      name: "A springy bike",
      type: "Mountain Bike"
    }, {
      name: "A springier bike",
      type: "Mountain Bike"
    }, {
      name: "An all-terain bike",
      type: "Mountain Bike"
    }, {
      name: "A classy bike",
      type: "Urban Bike"
    }, {
      name: "A modern bike",
      type: "Urban Bike"
    }, {
      name: "A blue bike",
      type: "Children Bike"
    }, {
      name: "A pink bike",
      type: "Children Bike"
    }, {
      name: "A noisy bike",
      type: "Children Bike"
    }, {
      name: "A clown bike",
      type: "Children Bike"
    }];

Tip

Downloading the example code

You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

Next, we will define a function that processes the dataset and creates an array of objects that contains the count of bicycles for a specific bicycle type:

var getBicyclesCountPerType = function() {
  var bicyclesCountPerType = [];
  bicycles.forEach(function(bicycle) {
    var isExistingType = false;
    bicyclesCountPerType.forEach(function(typeCount) {
      if (typeCount.type === bicycle.type) {
        typeCount.count += 1;
        isExistingType = true;
      }
    });
    if (!isExistingType) {
      bicyclesCountPerType.push({
        type: bicycle.type,
        count: 1
      });
    }
  });
  return bicyclesCountPerType;
};

The first highlighted function forEach is ES5 specific and enumerates the values of an array while executing a function for each element in the array. The forEach function is defined against Array.prototype, so it will be available to any Array instance.

The final code snippet for the first example relies on jQuery to append the results for each bicycle type as HTML content:

$(document).ready(function() {
  var outputContent = "There are " + bicycles.length + " bicycles:"
  var results = getBicyclesCountPerType();
  results.forEach(function(typeCount) {
    outputContent += "<br />";
    outputContent += " - " + typeCount.count + " of type: " + typeCount.type;
  });

  $("#output").html(outputContent);
});

Notice how we use forEach again to enumerate the final results. You can explore the example in the starter-example-with-ECMAScript5 folder from the source code for this chapter. In order to run this example, you need to open the index.html file in your preferred web browser. The example can also be executed directly at http://bit.ly/1WLzHuS.

You can view the example output in this screenshot:

The Underscore find starter example

In the previous example, we used two-nested forEach calls, and this approach seems inefficient under closer scrutiny. You cannot break a forEach loop, which means that you cannot stop after you processed the specific elements that were targeted. The second nested forEach call only needs to process a target element from the list rather than iterating the full list every time. Fortunately, Underscore has a function called find that iterates an array and stops when an element matching a specific criteria is found.

To use Underscore, we need to add a reference to a hosted version of the library in the head element of the example web page:

<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore.js"></script>

In the first example, we changed the function getBicyclesCountPerType() to use the Underscore find function:

var getBicyclesCountPerType = function() {
  var bicyclesCountPerType = [];
  bicycles.forEach(function(bicycle) {
    var currentTypeCount = _.find(bicyclesCountPerType, function(typeCount){
      return typeCount.type === bicycle.type;
    });
    if (currentTypeCount) {
      currentTypeCount.count += 1;
    }
    else
    {
      bicyclesCountPerType.push({
        type: bicycle.type,
        count: 1
      });
    }
  });
  return bicyclesCountPerType;
};

The code is terser and more efficient thanks to the find function, while the rest of the example remains unchanged. You can explore the full example in the starter-example-with-underscore.find folder from the source code for this chapter or by running it directly at http://bit.ly/1U7dVO4.

All Underscore functions are usually accessed through the Underscore global object named _. This name can be changed if it conflicts with other global variable names, and we will explore how to do this in Chapter 4, Programming Paradigms with Underscore.js. We will stick with the default Underscore global object name for most of the examples.

The full signature of the find function is _.find(list, predicate, [context]). This function accepts an array or an object as its first parameter list followed by a function parameter predicate that will be executed against each element of the array or against each property value of the object passed as the first parameter. The second parameter function predicate accepts an array element or an object property value and should return a Boolean value. If the return value is true, the _.find() function will stop iterating and will return the array element or the property value from the current position. The last optional parameter context is rarely used in this book and will be covered later on when we discuss scopes and the value of the this object. The find function signature was briefly explored here as it is used extensively in Underscore and subsequently in the rest of the examples.

Note

In ES6, there is also a find function defined against Array.prototype that can be used as an alternative. We will discuss this function and other similar ECMAScript 6 functions in Chapter 6, Related Underscore.js Libraries and ECMAScript Standards.

The Underscore countBy starter example

The previous example introduced Underscore as an incremental improvement compared to the example before that. Among Underscore's 100+ functions, there is one that can replace the getBicyclesCountPerType() function completely and significantly reduce the number of lines of code. This function is countBy and its signature is _.countBy(list, iteratee, [context]). While its signature is similar with the one for the find function, the second function parameter iteratee is expected to return a string value representing a key for the iterated item. The key is used to group different elements of an array or different object property values depending on the list parameter type.

The countBy function returns an object with properties that have names taken from the keys supplied by the iteratee function. The value of such a property is the count of items from the list parameter that share the current property name as key.

The getBicyclesCountPerType() function invocation can be completely replaced with the _.countBy() function invocation:

var result = _.countBy(bicycles, function(bicycle){
    return bicycle.type;
});

The result object value has the following JSON representation:

{
    "Road Bike": 2,
    "Mountain Bike": 3,
    "Urban Bike": 2,
    "Children Bike": 4
}

The code tasked with displaying the example output needs to be changed accordingly. Instead of manipulating the array returned by the getBicyclesCountPerType() function it should manipulate the result object properties. We will use another Underscore function _.pairs(), which converts object properties to an array of elements where each element is a property name and value pair. Such a pair is a two-dimensional array itself, and you can see how it is declared and referenced in the highlighted sections from the following example:

$(document).ready(function() {
  var outputContent = "There are " + bicycles.length + " bicycles:"
  var result = _.countBy(bicycles, function(bicycle) {
    return bicycle.type;
  });
  _.pairs(result).forEach(function(typeCountPair) {
    var key = typeCountPair[0];
    var value = typeCountPair[1];
    outputContent += "<br />";
    outputContent += " - " + value + " of type: " + key;
  });
  $("#output").html(outputContent);
});

We have now dramatically reduced the size of the initial example by using Underscore functions while maintaining the same functionality. You can explore the full example in the starter-example-with-underscore.countBy folder from the source code for this chapter or by running it directly at http://bit.ly/1JdNwc6.

We started with one example that uses ES5 functions, followed by one that uses an Underscore function that is also an ES6 function. The final example uses another Underscore specific function that provides a higher level of data manipulation compared to built in JavaScript functions. You should find a lot more Underscore examples of this kind throughout the book.

 

Key Underscore functions


Out of the many Underscore functions, there are a couple that deserve special attention as they provide a good introduction to a range of similar functions. These functions are essential when processing data, they have an equivalent in the ES5 specification, and they are: each, map, and reduce.

Underscore each

In the first example, we used the ES5 function Array.prototype.forEach to iterate the initial dataset. Underscore also provides its own version called _.each(). The main difference from the Array.prototype.forEach function is that _.each() can be used against object properties if an object rather than array is provided as its first argument. The full function signature is _.each(list, iteratee, [context]) and it will call the second function parameter iteratee against each item of the iterated list object or array passed as its first argument. To use this function in the second example starter-example-with-underscore.find from this chapter, we just need to replace this line from the getBicyclesCountPerType() function:

bicycles.forEach(function(bicycle) {

With line:

_.each(bicycles, function(bicycle) {

Underscore each has a forEach alias, so it can be called in a similar way with its ES5 equivalent:

_.forEach(bicycles, function(bicycle) {

Another difference from the ES5 equivalent is that Underscore each returns the first parameter as opposed to undefined allowing further function call chaining. Underscore also has first class support for function chaining that will be detailed in Chapter 4, Programming Paradigms with Underscore.js.

We will explore calling each over object properties together with other Underscore functions mentioned in this chapter in Chapter 2, Using Underscore.js with Collections.

Underscore map and reduce

The next Underscore functions of special interest are map and reduce. The Underscore map function signature is similar to the each function signature: _.map(list, iteratee, [context]). It transforms a list array or object parameter into a new array that contains modified elements or property values of the list parameter. The transformation is made by:

  • Iterating the elements or properties of the list parameter using the second parameter function iterate, and calling this function for each item;

  • Collecting the values of the iteratee call into the array returned by the _.map() function.

Underscore reduce is a function that converts an array or object properties into a single value. The function signature is _.reduce(list, iteratee, [memo], [context]) and the optional memo parameter is used as the seed for the returned value. If memo is not specified, then the first element or object property value of the list parameter will be used as the seed value instead. The iteratee function signature is iteratee(memo, element, index, list) when list is an array like object. The value returned by iteratee is either supplied as the memo parameter for the next iteration or is returned as the final reduce value if there are no more iterations left.

The next example will showcase these two functions by iterating over an array of people that have received an important award at some point in their lives—the Nobel prize for literature. The example calculates the age when the award was received using _.map() and the average value of the age when the award was received using _.reduce().

This is the initial dataset:

var people = [{
    name: "Herta Muller",
    birthYear: 1953,
    awardYear: 2009
  }, {
    name: "Mario Vargas Llosa",
    birthYear: 1936,
    awardYear: 2010
  }, {
    name: "Tomas Transtromer",
    birthYear: 1931,
    awardYear: 2011
  }, {
    name: "Mo Yan",
    birthYear: 1955,
    awardYear: 2012
  }, {
    name: "Alice Munro",
    birthYear: 1931,
    awardYear: 2013
  }, {
    name: "Patrick Modiano",
    birthYear: 1945,
    awardYear: 2014
  }];

First, we calculate the age when the award was received for each person by using _.map():

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

Next, we calculate the average age of the award by using _.reduce() and passing 0 as the memo seed value:

var totalAge = _.reduce(peopleWithAwardAge, function(memo, person) {
  return memo + person.awardAge;
}, 0);
var averageAwardAge = totalAge / peopleWithAwardAge.length;

You can explore the complete example in the underscore.map.reduce folder from the source code for this chapter or by running it directly at http://bit.ly/1MMSOlc.

 

Functional programming fundamentals


In the previous example, we transformed an existing dataset into a new structure rather than modifying it (mutating it) by using the two powerful Underscore functions _.map() and _.reduce(). This is a typical example of a functional programming style in action: we use functions also known as higher-order functions that accept other functions as parameters, and we don't alter (mutate) the state of variables such as people and peopleWithAwardAge once they are created—the data that we work with is immutable.

Functional programming (FP) is a declarative programming paradigm in which functions are first class citizens that are treated as values (data), and the application code avoids mutating existing states. In an FP language, you also find that:

  • Functions called higher-order functions can be used to compose other functions by accepting functions as arguments or by returning functions.

  • Functions that don't have side effects are called pure functions, and if they are called repeatedly with the same parameters, they return the same result. They don't have any dependency on data outside their function scope.

There are many aspects of FP such as extensive use of recursion, employing lazy evaluation, and built-in support for pattern matching that are implemented to various degrees in FP languages. Languages such as Erlang, Haskell, Clojure, and F# (to name just a few) are considered functional languages, while languages such as C#, JavaScript, and Java 8 have limited support for a functional programming style. Languages such as Scala are classified as object-functional—a bridge between FP and OOP (object-oriented programming) paradigms.

JavaScript can be used in a functional programming style through its built in support for functions as first class citizens, higher-order functions, by simulating immutability and using its limited recursion capabilities. We will explore JavaScript functional aspects in Chapter 4, Programming Paradigms with Underscore.js and where needed in other chapters.

 

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.

 

Setting up a development workflow for exploring Underscore


The examples we used so far can be executed directly and their external JavaScript dependencies such as jQuery and Underscore are referenced using publicly hosted files. This is great for quick examples and rapid prototyping but not a recommended practice when building any non-trivial application. We will next set up a development workflow that is popular with JavaScript developers. This workflow will help us build examples efficiently and is close to what a real-life JavaScript application will use.

Modern JavaScript development with Node.js

The process of building modern JavaScript applications is greatly simplified by leveraging the rich ecosystem powered by Node.js.

Node.js is a software runtime for building server-side applications using JavaScript. Internally, it uses the V8 JavaScript engine that powers the Google Chrome web browser to compile JavaScript code to native code before executing it. It is based on an asynchronous event-driven programming model with non-blocking I/O operations using a single thread of execution. This makes it a great fit for real-time applications and high-throughput web applications. Node.js benefits from all the optimizations and performance improvements that Google Chrome has introduced since its first version and it runs on all major operating systems: Linux, Mac OS X, and Windows.

The Node.js installation comes with a built-in package manager called npm that contains a very large number of packages. There is an online npm packages repository available at https://www.npmjs.org/ that is used for installing or updating Node.js application dependencies.

For setting up the Node.js environment, we will target the Windows operating system first with Mac OS X and Linux covered next.

Windows

You need to install Node.js, which comes in 32-bit and 64-bit versions by going to https://nodejs.org/download/, and select one of the packages that matches your OS. You can use https://nodejs.org/dist/v0.12.7/node-v0.12.7-x86.msi for the 32-bit version or https://nodejs.org/dist/v0.12.7/x64/node-v0.12.7-x64.msi for the 64-bit version (the available versions may be different at the time you are reading this book). We have used the 64-bit version with the examples from this book. As an alternative installation method, you can use the Chocolatey package manager for Windows and install it using the instructions provided at https://chocolatey.org or by executing the following line in a Command Prompt with administrator privileges (opened using the Run as administrator option):

@powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))" && SET PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin

With Chocolatey now available, we can use it to install Node.js by executing the following command:

choco install -y node.js

Mac OS X

On Mac OS X, we will use the Homebrew package manager for OS X that can be installed by following the instructions at http://brew.sh or by executing the following command line in the terminal:

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

With Homebrew now available, we can use it to install Node.js by executing the following command:

brew install node

Linux

Ubuntu 14.04 was the target Linux distribution used to test the examples from this book, but you should be able to use any Linux distribution you prefer. Ubuntu comes with its built-in package manager, and we will use it to execute the commands requires to register the Node.js repository used in this book (more details at http://bit.ly/1hcVaMm):

sudo apt-get install -y curl
curl -sL https://deb.nodesource.com/setup_0.12 | sudo bash -

With the Node.js repository now configured in Ubuntu, we can install Node.js with the following command:

sudo apt-get install -y nodejs

Verifying Node.js installation

To verify that Node.js has installed correctly, we need to open the OS-specific Command Prompt and execute the following command:

node -v

If you see v0.12.07 on the screen, it means that Node.js has installed correctly, and we can execute all the examples from this book without any other OS-level changes.

Note

We will explore more about Node.js and npm packages in Chapter 5, Using Underscore.js in the Browser, on the Server, and with the Database.

Managing JavaScript dependencies with Bower

With Node.js installed, we have access to its large repository of development tools and libraries provided through npm. We will use npm to install Bower, which is a package manager optimized for web sites and web applications. Bower is very similar to npm with the main difference being that Bower packages maintain a flat dependency tree as opposed to npm packages that maintain a deep nested dependency tree. For example, if two Bower packages have a dependency on Underscore, only one version of the Underscore package will be installed. If two npm packages have a dependency on Underscore, each package will install its Underscore package separately (although npm 3 will use flat dependencies by default similar to Bower).

Note

Bower has a dependency on the Git source control system, which can be installed from this location http://git-scm.com/downloads.

To install Bower, you need to open a Command Prompt and execute this npm command:

npm install -g bower

Notice the -g npm command-line switch, which installs the package in the machine specific npm packages folder. By default, npm packages are installed relative to the current directory unless this command switch is specified. From now on, Bower will be available to all our examples regardless of the folder in which they reside.

Next, we will use the previous example found in the underscore.map.reduce-revealing-module folder from the source code for this chapter and transform it into a new example that leverages Bower for package management.

By running the following command in the example folder, we will create a bower.json file that holds metadata about the current project and its dependencies:

bower init

When running this command, there are a series of Command Prompt options that can be accepted with their default values. The values will be updated in the bower.json file. Next, we will install the Bower packages that match the required JavaScript library dependencies for jQuery and Underscore:

bower install jquery#2.1.4 --save
bower install underscore#1.8.3 --save

Notice the package name jquery followed by the version target #2.1.4 that ensures that we are using only a specific version of a Bower package rather than the latest version available when we omit the version target. The last command-line switch --save will persist the package information in the bower.json file allowing an easy restore for the Bower packages if they get deleted locally or they are not committed with the source code for the current project.

We now have the two JavaScript libraries installed in the current folder within a bower_components subfolder, which is the default Bower location for storing packages.

We can now change the JavaScript references in the index.html file to point to the two local Bower packages rather than the online hosted files:

<script src="bower_components/jquery/dist/jquery.js"></script>
<script src="bower_components/underscore/underscore.js"></script>

I have also moved the inline JavaScript code into two separate files called awardAgeCalculator.js and index.js that separate the core functionality in the former from the output specific code in the latter. You can find the example in the underscore.map.reduce-with-local-dependencies folder from the source code for this chapter.

By leveraging Bower, we can quickly reference any JavaScript library that is needed while making it very easy to share the current project with other developers or even publish it to the Bower package repository.

Choosing a JavaScript editor

We are approaching the end of the first chapter, and so far, we have not mentioned any tool for editing the code used in the example. Actually, we don't really have to as modern JavaScript development relies on a command-line-based tool chain usually based on Node.js. The workflow we established for exploring the examples from this book does not require a specific visual editor to be installed. You could just use the default operating system text editor and stop at that. However, it is worth mentioning the existing rich ecosystem of JavaScript visual editors and IDEs that is available if you want to benefit from additional functionality.

There are both commercial and free to use tools available, and they are separated into two categories: tools that have a main focus on Node.js and JavaScript-based development, and tools for general development that support Node.js and JavaScript through a plugin system. Note that we mentioned both Node.js and JavaScript as requirements for any editor or IDE as Node.js will be used extensively later on in this book.

Out of the Node.js and JavaScript-specific commercial tools, it is worth mentioning WebStorm IDE (more details at https://www.jetbrains.com/webstorm/) and Cloud9 online IDE that allows browser-based JavaScript development among other languages (more details at https://c9.io/). There is even a free Microsoft Visual Studio plugin called Node.js Tools for Visual Studio development (more details at http://nodejstools.codeplex.com/).

Out of the editors supporting JavaScript via their plugin system, I will mention the commercial editor Sublime Text (free trial available, more details at http://www.sublimetext.com/) and the free and open editor Atom (https://atom.io/). Atom itself was built using Node.js, has a rich plugin ecosystem, and is the editor used to author all the code from this book. To install Atom, you can follow the instructions available at http://bit.ly/1EiGYwn.

Of course there are many other tools available out there, but these are the ones that I personally found useful for learning how to build JavaScript applications.

You can execute all the examples from this book without having to install or configure anything just by using Cloud9 IDE (free registration required) at the project address https://ide.c9.io/alexpop/underscorejs-examples.

 

Testing JavaScript code with Jasmine


Maintaining complex JavaScript codebases is challenging due to the dynamic nature of the language and the lack of built-in module support (up until ES6). Applying unit testing practices helps alleviate these issues and JavaScript as a language benefits from a large number of unit testing frameworks, libraries, and tools.

We will now add tests for the previous example found in the underscore.map.reduce-with-local-dependencies folder from the source code for this chapter. To implement these tests we will use Jasmine, a popular test framework.

Jasmine is a behavior-driven development (BDD) framework that contains extensive built-in functionality to define and execute tests for JavaScript code. A BDD framework differs from other test frameworks such as QUnit by defining tests as a desired behavior, where the test outcome is specified first followed by the actual test assertion. Jasmine uses a describe/it syntax to define test specifications, while other BDD frameworks use a given/when/then syntax. Using BDD tests produces output similar to a specification document and these types of tests are usually called specs. Other advantages of using Jasmine are that it does not rely on any other library, it has a rich functionality for defining tests and tests assertions, and it has great documentation available at http://jasmine.github.io.

Jasmine introduction

A typical Jasmine test that asserts the result of a trivial JavaScript operation will have the following code (with the Jasmine specific functions highlighted):

describe("A basic JavaScript add operation", function() {
  it("should be correct", function() {
    var result = 1 + 1;
    expect(result).toEqual(2);
  });
});

When running the test, its output should read A basic JavaScript add operation should be correct, forming a meaningful statement about the value delivered by the code being tested. The describe call is a Jasmine global function that will group one or more test specifications that are defined by the it function (which is also another Jasmine global function). Both functions have a test-related description as the first argument. The second argument is a function that defines the test suite in its body with the test specification (or the spec) defined in the it function. Test assertions use the Jasmine global function expect, which is chained with a helper function called matcher that will facilitate the test result evaluation.

There are a couple of built-in matcher functions available, such as toBe(), which checks whether the test assertion object and the expected object are the same; toEqual(), which checks whether the two objects are equivalent; and toBeDefined(), which checks whether the test assertion object is not undefined. You can also define your own custom matchers for more complex expectation checks. Jasmine allows you to set up and tear down data before and after a spec is executed through the global functions beforeEach() and afterEach().

Adding tests using the default Jasmine infrastructure

Before creating the tests, we need to modify the awardAgeCalculator.js file that contains the code under test (or SUTsystem under test) and ensure it is testable. A SUT is testable if it allows swapping out its dependencies, if it can be tested in isolation and does not depend on a shared or global application state.

For our example, we need to test that the two global accessible (or public) functions of the awardAgeCalculator object (the SUT) are producing the expected results when executed against specific data sets. Currently, we cannot easily swap out the default array of people used in the example and we need to change it by making the getPeople() function public and changing the rest of the functions to accept an input array as highlighted in the next code snippet:

var awardAgeCalculator = (function() {
  "use strict";

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

  return {
    getPeople: getPeople,
    calculateAwardAgeForPeople: function(people) {
      return _.map(people, function(person) {
        return {
          name: person.name,
          awardAge: person.awardYear - person.birthYear
        };
      });
    },
    getAverageAwardAgeForPeople: function(people) {
      var peopleWithAwardAge = this.calculateAwardAgeForPeople(people);
      return _.reduce(peopleWithAwardAge, function(memo, person) {
        return memo + person.awardAge;
      }, 0) / peopleWithAwardAge.length;
    }
  };
}());

We also exposed the getPeople() function to the global scope for convenient access to the default data set. By making these changes, we have created a testable SUT where we can alter the input data for the two functions we plan to test. We can now write the tests by creating a spec\awardAgeCalculatorSpec.js file and using the following code:

describe("Given awardAgeCalculator", function() {
  describe(
    "when calling calculateAwardAgeForPeople()",
    function() {
      var people;
      var peopleWithAwardAge;
      beforeEach(function() {
        people = awardAgeCalculator.getPeople();
        peopleWithAwardAge = awardAgeCalculator.calculateAwardAgeForPeople(people);
      });
      it(
        "then the award age for the first person should be correct",
        function() {
          expect(peopleWithAwardAge[0].name).toEqual("Herta Muller");
          expect(peopleWithAwardAge[0].awardAge).toEqual(56);
        });
      it(
        "then the award age of the last person should be correct",
        function() {
          expect(peopleWithAwardAge[peopleWithAwardAge.length - 1].name).toEqual("Patrick Modiano");
          expect(peopleWithAwardAge[peopleWithAwardAge.length - 1].awardAge).toEqual(69);
        });
    });
  describe(
    "when calling getAverageAwardAgeForPeople()",
    function() {
      var people;
      var aveargeAwardAge;
      beforeEach(function() {
        people = awardAgeCalculator.getPeople();
        aveargeAwardAge = awardAgeCalculator.getAverageAwardAgeForPeople(people);
      });
      it("then the average award age should be correct", function() {
        expect(Math.floor(aveargeAwardAge)).toEqual(69);
      });
    });
});

The tests are defined within two nested describe functions and we used a beforeEach function to avoid code duplication when exercising the SUT. The expectations for the first set of tests are verifying that the person name and the award age are correct.

To execute these tests, we need to add Jasmine support to our example. We will use Bower to install Jasmine as a development package (a package that can be omitted when the current project is deployed to a target environment) through the following command:

bower install jasmine#2.3.4 --save-dev

We will change the default SpecRunner.html file provided with the standalone Jasmine distribution at http://bit.ly/1EhdgHT to reference the files from the Bower package together with the SUT file (the code file) and the test file as highlighted in the following code:

<!DOCTYPE HTML>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>Jasmine Spec Runner v2.3.4</title>
  <link rel="shortcut icon" type="image/png" href="bower_components/jasmine/images/jasmine_favicon.png">
  <link rel="stylesheet" type="text/css" href="bower_components/jasmine/lib/jasmine-core/jasmine.css">
  <script type="text/javascript" src="bower_components/jasmine/lib/jasmine-core/jasmine.js"></script>
  <script type="text/javascript" src="bower_components/jasmine/lib/jasmine-core/jasmine-html.js"></script>
  <script type="text/javascript" src="bower_components/jasmine/lib/jasmine-core/boot.js"></script>

  <!-- include source files here... -->
  <script src="bower_components/underscore/underscore.js"></script>
  <script type="text/javascript" src="awardAgeCalculator.js"></script>
  <!-- include spec files here... -->
  <script type="text/javascript" src="spec/awardAgeCalculatorSpec.js"></script>
</head>
<body>
</body>
</html>

Notice that we referenced Underscore as a SUT dependency and we don't need any special test output code to display the results other than ensuring that all required JavaScript files are referenced in the SpecRunner.html file. You can find the example in the underscore.map.reduce-with-jasmine folder from the source code for this chapter.

To run the tests, we just need to open the SpecRunner.html file in a browser and we should see this output:

Jasmine tests can also be executed automatically through test runners such as Karma (http://karma-runner.github.io/) or Node.js build tools such as Grunt or Gulp. We will discuss these topics in Chapter 5, Using Underscore.js in the Browser, on the Server, and with the Database and Chapter 6, Related Underscore.js Libraries and ECMAScript Standards.

 

Summary


This chapter provided an introduction to Underscore and explored useful practices and patterns that will be used throughout the book. We established a development workflow for working efficiently with Underscore examples and we finished the chapter by introducing testing with Jasmine.

Based on these fundamentals, in the next chapter, we will start exploring the Underscore functionality for collections in detail.

About the Author
  • Alexandru Vasile Pop

    1. The author is a professional software developer with 15 years of experience in building applications for various platforms and technologies. 2. The author has worked for ISVs building enterprise resource planning applications, insurance and financial software products, a content management system, and in the higher education sector as a web application developer. 3. He is the author of two programming books “Learning Underscore.js” and “Learning AngularJS for .NET developers”. His developer blog at alexvpop.blogspot.com contains technical articles around .NET, JavaScript, and various software engineering topics. 4. I would like to thank my wife and daughter for their support and motivation.

    Browse publications by this author
Latest Reviews (3 reviews total)
Parfait très bons documents et service WEB impeccable
not finish entirely but so far good.
One of the best book to discover the library fundamentals
Learning Underscore.js
Unlock this book and the full library FREE for 7 days
Start now