Blogs

This Blog

News

Favorite Posts

Archives

ctodx

Discussions, news and rants from the CTO of DevExpress, Julian M Bucknall

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

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.

Published Apr 05 2011, 06:42 PM by
Filed under: , ,
Bookmark and Share

Comments

David Chai Kam Fook

Wow! This is first time I bookmark your article. Thank you.

April 7, 2011 6:11 AM

About Julian Bucknall (DevExpress)

Julian is the Chief Technology Officer at Developer Express. You can reach him directly at julianb@devexpress.com. You can also follow him on Twitter with the ID JMBucknall.
LIVE CHAT

Chat is one of the many ways you can contact members of the DevExpress Team.
We are available Monday-Friday between 7:30am and 4:30pm Pacific Time.

If you need additional product information, write to us at info@devexpress.com or call us at +1 (818) 844-3383

FOLLOW US

DevExpress engineers feature-complete Presentation Controls, IDE Productivity Tools, Business Application Frameworks, and Reporting Systems for Visual Studio, along with high-performance HTML JS Mobile Frameworks for developers targeting iOS, Android and Windows Phone. Whether using WPF, Silverlight, ASP.NET, WinForms, HTML5 or Windows 8, DevExpress tools help you build and deliver your best in the shortest time possible.

Copyright © 1998-2014 Developer Express Inc.
All trademarks or registered trademarks are property of their respective owners