This post is over 6 months old. Some details, especially technical, may have changed.

Underscore.js: The JavaScript Utility Library

I've been meaning to give Underscore.js some love for some time but never got around to it.  The other day I though about writing about Backbone.js (another excellent library from the same people) but didn't feel it was possible to do so without first talking about Underscore as it underpins most of the Backbone.js functionality.  So without further ado...

Introduction

Underscore is a utility-belt library for JavaScript that provides a lot of the functional programming support that you would expect in Prototype.js (or Ruby), but without extending any of the built-in JavaScript objects. It's the tie to go along with jQuery's tux.

- http://documentcloud.github.com/underscore/

That's how the creators describe underscore and it's pretty spot on.  Underscore offers numerous low level functions that can be used to perform on arrays and collections (as well as few for object and functions).  Some of these functions are available in some browsers but not all and because of this Underscore provides a nice layer of abstraction to avoid having to constantly feature detect.  Incidentally where a browser does implement a specific function Underscore will always fallback on the native implementation ensuring that performance is as optimal as possible.  This is the sort of stuff that you would probably miss if you moved from Prototype to jQuery.  Another point to note is that Underscore doesn't extend native objects with it's own implementations which most people will consider a good thing.  Instead, like jQuery, Underscore exposes a single global object aptly named _.  Now I am not 100% sold on this, I am a bit strange when it comes to neat code and I think this variable name makes code look a bit "noisy".  Not a big deal but I like to pick holes in things :-).

Coding Styles

There are two ways to write code using Underscore.  Imagine the scenario that given an array of numbers You can go the functional route and call methods directly on the underscore object,

var filtered = _.select([1,4,2,5,78,23], function(v){
    return v > 10;
});

var sum = _.reduce(filtered, function(memo, v){ 
    return memo + v; 
}, 0);

console.log(sum)
// => 101

Or go the OO route for those familiar with the jQuery style syntax, 

var sum = _([1,4,2,5,78,23]).chain()
    .select(function(v){ return v > 10; })
    .reduce(function(memo, v){ return memo + v; }, 0))
    .value();
    
console.log(sum)
// => 101    

These 2 examples acheive the same thing - filter a list to those values greater than 10 and derive a sum of these values; but the look and feel are actually quite different.  Which one you chose will be down to personal preference (I tend towards the former approach but the jury is still out).

Collections/Arrays

Underscore has a ton of collection and array methods -  a collection being either an array or an object (as in an object is essentially a property bag/associative array). 

The usual suspects for array manipulation/traversal are all here.  Coming from a C# background some of the names will be familiar and some are different but they achieve the same thing - here is just a tiny subset,

each()

Loops over a collection executing a callback on each element.  break; support provided by _.breakLoop()

_([2,4,6,8,11,13,17]).each(function(num) {
  if(isPrime(num)){
    console.log("First prime: ", num);
    _.breakLoop();
  }
});
=> First Prime: 11

map()

Creates a new array from a collection where each element can be transformed by a callback

var result = _([2,4,6,8,11,13,17]).map(function(v) {
    return isPrime(v) ? true : false;
});

console.log(result)
//=> [false,false,false,false,true,true,true]

first()/last()

Select the first and last elements from an array

var arr = [2,4,6,8,10];

console.log(_.first(arr));
console.log(_.last(arr));
//=> 2
//=> 4

Functions

Underscore provides a number of methods that can be applied to functions.  These include the ability to bind methods to a specific object/context (bind), memoize functions (caching or results to increase calculation speeds), delay and defer execution of methods and perform some AOP style actions on functions (wrap and compose).

Objects

Underscore provides a number of common object related methods such as the ubiquitous extend and clone functions.  Other useful functions include the use of a tap function.  This function lets you "tap" into an execution chain and inspect/manipulate the object in its current state.  The underscore example demonstrates this perfectly.

_([1,2,3,200]).chain()
  .select(function(num) { return num % 2 == 0; })
  .tap(console.log)
  .map(function(num) { return num * num })
  .value();

//=> [2, 200]
//=> [4, 40000]

As you can see the tap function is used to push the object (in this case an array) through.  There is also a fairly robust set of type detection functions.

isEqual, isEmpty, isElement,isArray, isArguments, isFunction, isString, isNumber, isBoolean, isDate,isRegExp isNaN, isNull, isUndefined

Other Things

There are a few other specific methods in Underscore that provide utility functions including,

  • template - super simple templating function
  • mixin - extend underscores functionlaity with your own custom methods
  • times - execute a callback a specified number of times

Conclusion

This is a very high level overview of Underscore (so I can now go and get all excited abuot Backbone) but hopefully you can see it's worth.  Have a look at the comprehensive docs that many of my example where derived from and see if there is anything useful for you there.

Published in JavaScript on November 12, 2010