Hi-performance Javascript Tips #1 [Updated 2009-03-19]

The arguments property of the Function object looks a lot like an array, but it’s really not. We say it’s array-like since it has a length property and properties whose names are whole numbers.

Similary, DOM properties, such as Element.childNodes, return a NodeList, which is also array-like.

If you’ve ever been frustrated that you can’t use array methods on these array-like collections, then the following function is your new friend:

function toArray (obj) { 
    return [].slice.call(obj, 0); 
}

I like this version because it’s so compact! It’s also very fast since it runs at compiled-code speeds. Here’s a version that even faster because it eliminates the construction of the stub array ([]) each time:

(function (ns) {
    var _a = [];
    ns.toArray = function (obj) { 
        return _a.slice.call(obj, 0); 
    }
})(window);

I used a closure to hide the stub array and keep the global namespace clean. Pass-in your own object / namespace (instead of window) to use the toArray function as a method on that object.

Most Javascript libraries have this feature built-in already, but this first example is one of my all-time favorites since it’s so short!

[Update: kangax improved my toArray function greatly (see discussion). Here are the results:]

var toArray = (function (slice) {
    return function toArray (object) {
        return slice.call(object, 0);
    }
})(Array.prototype.slice);

I remember the first time I saw something like this. It made my head ache. It’s best to go line by line, starting at the middle:

return slice.call(object, 0);

If you’ve understood the post this far, you probably already know that “call” is a method of the Function prototype that executes its function in the scope of another object. Here, we’re executing slice in the context of our array-like object. Any remaining parameters to the call method are passed as formal parameters to slice. Here, calling slice with zero for the first parameter and no second parameter just tells slice to grab all items.

return function toArray(object) {

The line immediately above declares our toArray function as well as the formal parameter for our array-like object. The return statement indicates that we’re going to pass our toArray function’s reference somewhere. Of course, it’s going to be assigned to our top-level toArray variable, but first let’s investigate the first and last lines:

var toArray = (function (slice) {
   . . .
})(Array.prototype.slice);

Here, we’re declaring an anonymous function with one parameter, slice. Then we’re executing the function, passing it a reference to Array.prototype.slice. This is an easy way to declare and assign a local variable to the slice function in our anonymous function. (The parentheses around the anonymous function cure a syntax ambiguity.) Since we’re executing the anonymous function immediately (inline execution), we’re returning the value from the outer return statement (return function toArray) directly into the outer toArray variable.

In case you missed it: the outer function executes immediately, returning the inner function (toArray), which will be executed when we call our [inner] toArray function — the one referenced in the toArray top-level variable! (If this is your first time doing this crazy s**t, you better re-read that a few times.)

kangax brings up an excellent point. Debuggers don’t typically introspect very deeply (and certainly won’t grok this construct). Therefore, it is important to name any non-trivial functions (e.g. our inner function) so that the name can be shown in stack traces, etc. A stack trace full of calls to function () aren’t very helpful. However, it is also important not to name any functions that may pollute our global namespace (e.g. our outer, inline-executed anonymous function).

OK. Now that that’s settled, I will attempt to make our toArray function as small as possible. In the early 90’s when I worked with an APL programmer. He always claimed he could write a whole application in 1 line of code. It turned into a big joke around the office. But I guess I took it to heart. Here’s my attempt at the smallest, high-performance, non-polluting, general-purpose toArray function:

var toArray = (function(s){return function(o){return s.call(o,0)}})([].slice);

Go ahead. Beat that.

(Yah. I know I am breaking so many best practices here, but I just had to try it!)

OK. So why is this so important?

So what makes this better than just placing calls to Array.prototype.slice.call(obj, 0) all over your code?

Reason #1: Clarity. The reader doesn’t get bogged down by implementation details.

Reason #2: Speed. At execution time, there are less lookups by the Javascript interpreter (2 versus 4), but only one extra reference to a local variable (s). References to local variables are much faster than lookups. Lookups occur every time you reference a member (every “.” in dot notation) and every time you reference a global variable.

Reason #3: Light weight. Calling toArray(obj) yields smaller code than using Array.prototype.slice.call(obj, 0). It only takes 4 calls to toArray before your code starts getting smaller.

Reason #4: Understandable. You won’t perturb the Java Stoics who have to understand your code. 😛

Be Sociable, Share!

3 thoughts on “Hi-performance Javascript Tips #1 [Updated 2009-03-19]

  1. Pingback: Hi-Performance Javascript Tip #2 Revisited | Unscriptable.com

  2. There’s really no need to pass `window` to self-executing function 🙂 Remember that `this` always refers to a global object when function is called as a function (at least in ES3). I also don’t see a reason to create an array just to use its `slice`. Besides, why resolve `slice` property lookup on `_a` (every time function is called) when you can store it as it is:

    (function(){
    _slice = Array.prototype.slice;
    this.toArray = function(object) {
    return _slice.call(object, 0);
    }
    })();

    We can, of course, shorten this up a bit:

    var toArray = (function(slice){
    return function(object) {
    return slice.call(object, 0);
    }
    })(Array.prototype.slice);

    It’s also a good idea to give function a descriptive identifier (for debugging purposes):

    var toArray = (function(slice){
    return function toArray(object) {
    return slice.call(object, 0);
    }
    })(Array.prototype.slice);

    • Hey kangax,

      There’s really no need to pass `window` to self-executing function Remember that `this` always refers to a global object when function is called as a function (at least in ES3).

      Drat! That’s what happens when you commit code at 2 AM after a 2 beers. I was going to discuss using the anonymous function parameters as another method of bringing variables into scope without polluting the outer scope. But I decided to cut the post short and shoved that craziness in there anyways.

      I also don’t see a reason to create an array just to use its `slice`.

      I was trying to see how few characters I could use. [] is much shorter than Array.prototype, but of course comes at the cost of an object construction.

      Besides, why resolve `slice` property lookup on `_a` (every time function is called) when you can store it as it is:

      /me blames it on the beers. 😛

      var toArray = (function(slice){
      return function toArray(object) {
      return slice.call(object, 0);
      }
      })(Array.prototype.slice);

      Awesomeness all around. Thx! I will update my post!

Comments are closed.