Rubyists: Meet Underscore.js, your new favorite JavaScript library

  Wynn Netherland • 2009-11-09

One of my favorite aspects of developing in Rails is the console. The ability to load and interact with my Ruby objects without using the browser is powerful.

Thanks to Firebug for Firefox and Web Inspector for Webkit, I also have a console for my client-side JavaScript. I wish jQuery were as powerful when traversing raw JavaScript objects as it is traversing the DOM. Well it seems I have two wishes left for the first has been granted in Underscore.js.

Underscore.js: 2kb of pure JavaScript horsepower!

Underscore.js is a new JavaScript framework from DocumentCloud that brings a powerful set of utility functions to JavaScript with a disctinct Ruby flavor and without monkey-patching the native JavaScript types by extending via prototype. Just as Prototype.js and jQuery employ the $ function, Underscore adds all of this goodness via the _ function.

So, what's in the box? Let's just ask _ himself, Ruby style:

>>> _.methods();
=> ["all", "any", "bind", "bindAll", "breakLoop", "clone", "compact",
"compose", "defer", "delay", "detect", "each", "every", "extend", "filter",
"first", "flatten", "foldl", "foldr", "forEach", "functions", "identity",
"include", "indexOf", "inject", "intersect", "invoke", "isArray",
"isElement", "isEmpty", "isEqual", "isFunction", "isUndefined", "keys",
"last", "lastIndexOf", "map", "max", "methods", "min", "pluck", "reduce",
"reduceRight", "reject", "select", "size", "some", "sortBy", "sortedIndex",
"template", "toArray", "uniq", "uniqueId", "values", "without", "wrap",
"zip"]

Nifty, huh? That should look very familiar. Let's look at map as an example.

>>> _.map([1, 2, 3], function(num){ return num*num });
=> [1, 4, 9]

Pretty straightforward. But we can also call Underscore OOP style:

>>> _([1, 2, 3]).map(function(num){ return num*num });
=> [1, 4, 9]

What's the advantage you ask? Chaining!

>>> _([1, 2, 3]).chain().map(function(num){ return num*num
>>> }).size().value();
=> 3

Whoa! Why the value() at the end?

When chaining, Underscore returns a wrapped set, a special container object (like jQuery does for wrapped sets of elements returned via a selector). To get the last value in the chain, simply call value.

What else is in the box?

Many of the same Ruby Enumerable and Array functions you've come to know and love are implemented including: each, map, detect, select, reject, all, any, include, max, min, sortBy, toArray, size, first, last, compact, flatten, uniq, and zip plus some new ones you'll love.

Underscore also includes also some really useful utility functions.

The bind method

bind a function to a context object, meaning that whenever the function is called, the value of this will be the context. Optionally, bind arguments to the function to pre-fill them, also known as currying.

var func = function(greeting){ return greeting + ', ' + this.name }; func =
_.bind(func, {name : 'Adrian'}, 'Yo'); func(); => "Yo, Adrian"

The template method

Compiles JavaScript templates into functions that can be evaluated for rendering. Useful for rendering complicated bits of HTML from JSON data sources. Template functions can both interpolate variables, using <%= … %>, as well as execute arbitrary JavaScript code, with <% … %>. When you evaluate a template function, pass in a context object that has properties corresponding to the template's free variables. If you're writing a one-off, you can pass the context object as the second parameter to template in order to render immediately instead of returning a template function.

Here's an example from the docs:

var list = "<% _.each(people, function(name) { %> <li><%= name %></li> <%
}); %>"; _.template(list, {people : ['moe', 'curly', 'larry']}); => "
<li>moe</li> <li>curly</li> <li>larry</li> "

Download and enjoy

Underscore.js is available in both production and development flavors.

Wynn Netherland
Wynn Netherland

Engineering Director at Adobe Creative Cloud, team builder, DFW GraphQL meetup organizer, platform nerd, author, and Jesus follower.