blog

underscore.js

by | Feb 18, 2013 | Developer Blog | 2 comments

Underscore JS logo

Have you ever had to deal with javascript code that was less than elegant and performed like it looked? I was waist deep in this mass of suffering when a colleague of mine introduced me to underscore.js. It is simply brilliant. The library is composed of about 80 functions that provide functional programming support without extending the built-in javascript objects. What makes this library stand out is that it delegates to built-in functions if they are present (i.e. if your browser or javascript engine supports it). There are several benefits of using underscore.js. Here are three:

  1. a performance boost due to the execution of native code;
  2. your code won’t break even if a particular browser or javascript engine doesn’t yet support the ECMA Script 5 specification;
  3. more intuitive and concise javascript code.

A first look

What I’d like to do with this post is whet your appetite for underscore.js instead of regurgitate what one could find in the underscore.js documentation.  With that in mind I will attempt to cover only a few functions that were most useful to me. I will specifically focus on the integration of underscore.js, the functions .each, .map and _.filter.

Integration

Adding underscore.js is as easy as downloading the source code from http://underscorejs.org/, placing it in a location that is accessible by the process that will be using it and making calls to its functions. For those who use web development frameworks, please consult your framework’s documentation for where third party javascript libraries should be placed.

_.each

The .each function allows for iteration through a collection in a more semantically meaningful way and will call the native function if it is supported. Sounds kind of boring but it allows for the decoupling of code. Consider code that might be using jQuery to manipulate DOM elements that needs to be used in multiple places (in the UI and on the back-end). With .each one can place this logic in a module that is more generalized and may be shared between the UI and a server side process. As a side note, one should use caution when iterating with closures. It’s a bad idea when performance is critical since each closure adds a level to the scope chain and each one needs to be checked. See https://developers.google.com/speed/articles/optimizing-javascript for more about the performance penalty of closures.

// Old school for example...
//
function DoCoolStuff(item)
{
   // Some stuff that is cool
}
for (index = 0; index < maxCount; ++index)
{
   DoCoolStuff(collection[index], index);
}

 

// Using _.each
//
function DoCoolStuff(item)
{
   // Some stuff that is cool
}
_.each(collection, DoCoolStuff);

As you can see from my example, what needs to be expressed in several lines of code can be done with one line. The code that uses _.each is elegant, far more meaningful, and it will be as efficient as it can be for the given execution environment.

_.map

The .map function exposes the ability to “map” or transform a list by executing a user specified function. .map is a bit of a blast from the past for me because this function is almost like the C++ STL algorithm transform. It provides a powerful way to efficiently iterate through a collection performing some operation on each element of the collection. It will also call to the native function if it is supported.

// Old school map or transform values
//
function Transformer(item)
{
   // Some stuff that is cool
}
var transformed = new Array();
for (index = 0; index < maxCount; ++index)
{
   transformed.push(Transformer(collection[index]));
}

 

// _.map to transform items in an element
function Transformer(item)
{
   // Some stuff that is cool
}
var transformed = _.map(collection, Transformer);

Like the .each function .map provides a more expressive and concise syntax for what is truly happening in the code. It’s impressive to me that the creators of underscore.js have thought through the issues presented to software developers with an eye for best practices in software development. It’s not just a collection of hacked together functions that aren’t well thought-out. The code that uses these functions immediately benefits from the engineering practices of the library itself, making the derivative work more robust.

_.filter

The _.filter function iterates over the provided collection, returning an array of all the values that meet the criteria of a user specified function. If it is supported, the ECMA Script filter function is used. The user specified function must be a binary function (it must return true or false.)

// Filter old school
//
function FilterCondition(item)
{
   // Filter it
}
var selected = new Array();
for (index = 0; index < maxCount; ++index)
{
   if (FilterCondition(collection[index]))
   {
      selected.push(collection[index]);
   }
}

 

// _.filter
//
function FilterCondition(item)
{
   // Filter it
}
var selected = _.filter(collection, FilterCondition);

We can see how compact the old scholl code has become by using .filter. We can also see that the use of .filter forces the user of the function to have better separation of behaviors within their javascript code instead of having massive blocks of code that are at best tough to read and at worst introduce odd side-effects.

Final thoughts

I hope these few examples have highlighted the fact that underscore.js is one of those libraries that we should “just use”. It adds no overhead, is trivial to integrate and makes your code more robust even if you don’t grasp all of the underlying engineering principles employed by underscore.js. Your mileage may vary of course. Each development project is unique and has its own set of constraints and challenges. I still encourage you to tinker with it on your own. Perhaps your next project will include underscore.js.

+ more

Accurate Timing

Accurate Timing

In many tasks we need to do something at given intervals of time. The most obvious ways may not give you the best results. Time? Meh. The most basic tasks that don't have what you might call CPU-scale time requirements can be handled with the usual language and...

read more