Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Reading Code: underscore.js

DZone's Guide to

Reading Code: underscore.js

· Web Dev Zone
Free Resource

Learn how to build modern digital experience apps with Crafter CMS. Download this eBook now. Brought to you in partnership with Crafter Software

I've been spending a bit of time reading through the source code of underscore.js, a JavaScript library that provides lots of functional programming support which my colleague Dave Yeung pointed out to me after reading my post about building a small application with node.js.

I'm still getting used to the way that JavaScript libraries are written but these were some of the interesting things that I got from reading the code:

  • There are a couple of places in the code where the author has some code which runs conditionally and this is achieved by including that expression on the right hand side of an '&&'.

    For example on line 129 in the 'filter' function:


  // Return all the elements that pass a truth test.
// Delegates to JavaScript 1.6's native filter if available.
_.filter = function(obj, iterator, context) {
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
var results = [];
each(obj, function(value, index, list) {
iterator.call(context, value, index, list) && results.push(value);
});
return results;
};

I would probably have used an if statement to check the result from calling 'iterator' but this way is more concise and pretty neat.

The same type of thing is done on line 150 in the 'every' function:

  _.every = function(obj, iterator, context) {
iterator = iterator || _.identity;
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
var result = true;
each(obj, function(value, index, list) {
if (!(result = result && iterator.call(context, value, index, list))) _.breakLoop();
});
return result;
};

The result is collected and the loop will also exit if the value of 'result' is ever false which is again a cool way to organise code.

It's also quite cool that you can assign a value to a variable from within a conditional – this isn't possible in any of the other languages that I've used previously as far as I'm aware.

It's even more evident in the 'max' function:

  _.max = function(obj, iterator, context) {
if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
var result = {computed : -Infinity};
each(obj, function(value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
computed >= result.computed && (result = {value : value, computed : computed});
});
return result.value;
};

'result' is conditionally assigned on line 196 but only if the computed value is greater than the current computed value. Again an if statement is avoided.

Another interesting thing about this function is that it specifically checks the type of the 'obj' passed in which reminded me about the discussion around Twitter having those sorts of checks in their Ruby code around a year ago. I guess that type of thing would be more prevalent in library code than in an application though.

I hadn't come across the !! construct which is used to turn a JavaScript expression into its boolean equivalent:
  _.isArray = nativeIsArray || function(obj) {
return !!(obj && obj.concat && obj.unshift && !obj.callee);
};

Without using '!!" the expression would return 'undefined' in the case that one of those functions on 'obj' was not set. '!!' forces the return value to be 'false'.

Another technique used in this code base is that of dynamically adding methods to the prototype of an object:
  // Add all mutator Array functions to the wrapper.
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
var method = ArrayProto[name];
wrapper.prototype[name] = function() {
method.apply(this._wrapped, arguments);
return result(this._wrapped, this._chain);
};
});
This is quite a cool use of meta programming although it isn't initially obvious how those functions end up on the object unless you know what to look for. It does significantly reduce the code needed to add these functions to the 'wrapper' object's prototype, avoiding the ' same whitespace, different values' problem that Neal Ford outlines in his article about harvesting idiomatic patterns in our code.

Crafter is a modern CMS platform for building modern websites and content-rich digital experiences. Download this eBook now. Brought to you in partnership with Crafter Software.

Topics:

Published at DZone with permission of Mark Needham, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}