Webinar wrap-up: Julian on JavaScript - reading underscore.js

ctodx
05 April 2011

This was kind of an experimental webinar: take some well-known library and read to code to see what we can learn from it. I’m a firm believer in learning tips and tricks, as well as syntax and shortcuts, from reading some well-written (or even badly-written!) codebase.

I chose underscore.js for this first experiment mainly because it’s a set of utility functions that’s pure JavaScript and that doesn’t use the DOM. I would download it and follow along with the video (URL to come, but it’ll be on DevExpress Channel) and with my notes here.

1. The source is structured like this:

(function() {
  // do some work
})();

A common JavaScript pattern: auto-executing anonymous function. The reason it’s wrapped in parentheses is so that the JavaScript interpreter doesn’t think that you are declaring a function statement: function foo() {}. The whole of underscore is inside this function, hence any local varaibles defined within it will be visible throughout this function, but not outside. Also any nested functions will also see this function’s locals.

2. First statement gets and saves the global object:

  var root = this;

The this variable points to the global object because the outer anonymous function was called using the function invocation. (In essence, it was not called on an object via the dot syntax, which is method invocation.) So we’re saving a reference to the global object. The reason for doing it this way, rather than, say, using window is that this code can also be used on the server, where the global object is called something else.

3. The we save the current value of the ‘_’ global variable. ‘_’ is the main entry point to all of underscore’s functionality. (Yes, it looks weird, so I shall use underscore.) If you are using another library that uses ‘_’ then we’re about to overwrite it. Underscore therefore gives you a way of getting it back: the noConflict method. It’s defined like this:

  _.noConflict = function() {
    root._ = previousUnderscore;
    return this;
  };

If you call it, you will get back the underscore object (actually, as we’ll see, it’s a function).

4. Next we have a bunch of code that sets up a set of local variables that point to standard functions and methods. The reason for this is (a) less typing, and (b) more speed. Every time, JavaScript has to resolve something like “foo.bar.baz” it has to search the invocation object (a hash table, like all objects) for “foo” (when you call a function JavaSxcript sets up a special object called the invocation object that holds the locals, etc.). Once it has a reference to that, it then has to lookup and find the “bar” object. Once it has that, it then has to look up and find the “baz” object. Agreed they’re hash table lookups, but it’s still slower than being able to compile to a static pointer.

5. Now we get the creation of the ‘-‘ object itself.

  var _ = function(obj) { return new wrapper(obj); };

This is where I complained that the convention in JavaScript is to name constructor functions with an initial capital letter. This is a mnemonic to help programmers realize that they can’t call the function directly, but have to use the constructor invocation (that is, with “new”). So, –1 for underscore there. But note that ‘_’ is a function, not an object, although later on we will be adding methods to this function.

6. This is where we expose the local ‘_’ function globally:

    root._ = _;

7. And now we come to each/forEach:

  var each = _.each = _.forEach = function(obj, iterator, context) {
    if (obj == null) return;
    if (nativeForEach && obj.forEach === nativeForEach) {
      obj.forEach(iterator, context);
    } else if (_.isNumber(obj.length)) {
      for (var i = 0, l = obj.length; i < l; i++) {
        if (iterator.call(context, obj[i], i, obj) === breaker) return;
      }
    } else {
      for (var key in obj) {
        if (hasOwnProperty.call(obj, key)) {
          if (iterator.call(context, obj[key], key, obj) === breaker) return;
        }
      }
    }
  };

This is the fundamental function in the whole of underscore. If you look through the source, you’ll see calls to this function (usually using its local name, each) all over the place. Worth exploring this code, in other words.

8. I went off on a tangent here about the use of “==” in this code. Essentially, I’ve learned that it’s bad to use “==” since coercion of the types either side of the operator can and will happen when you least expect. It turns out that if obj is either undefined or null, the condition is true, but it’s false otherwise. A handy trick to know, although I may find that I don’t use it.

9. What the heck is breaker? Turns out that it’s a dummy object returned by internal uses of each to break out of the loop early. (Check out every and any for where it’s used.) It’s a neat use of a dummy object, something that any outside code can’t use or misuse.

10. Notice that if we are iterating over an array (or an array-like object) we use for, but over an object, we use for..in, and make sure we call hasOwnProperty to identify the “local” properties and not those inherited through the prototype chain.

11. I found invoke interesting because it allows you to pass in any number of extra parameters to the method you call on every element.

  _.invoke = function(obj, method) {
    var args = slice.call(arguments, 2);
    return _.map(obj, function(value) {
      return (method ? value[method] : value).apply(value, args);
    });
  };

Since the invoke method requires the first two parameters itself, we have to remove them to create our own arguments array that we then “apply” to the method call. We do this via the slice function, which, if you follow it back, comes from the Array prototype. Nifty.

12. The rest of my notes were kind of minor, although I did like the implementation of isNaN since it makes use of the fact that NaN is the only value in JavaScript that is not equal to itself.

  _.isNaN = function(obj) {
    return obj !== obj;
  };

And similarly isBoolean since there are only two possible values.

  _.isBoolean = function(obj) {
    return obj === true || obj === false;
  };

All in all, a nicely written library, I feel. The only two things I’d change would be the name of the constructor function wrapper and (possibly) the name of the global object ‘_’. That last one just looks weird to me, even now.

So, did you find the premise of this webinar interesting? Reason I ask is, at the end of the month, I’ll be doing the same with jQuery, so let me know if you’d like a different approach or not.

Free DevExpress Products - Get Your Copy Today

The following free DevExpress product offers remain available. Should you have any questions about the free offers below, please submit a ticket via the DevExpress Support Center at your convenience. We'll be happy to follow-up.
No Comments

Please login or register to post comments.