Arguments.callee considered extraneous

June 2nd, 2010

There is a fairly well known method anonymous recursion in Javascript. Every Javascript function has access to an implicit arguments object, and attached to that is the the callee property which holds the function being currently being invoked.

You might see arguments.callee used in a situation similar to this:

var items = [1,2,3,4,5,6,7];

// As we process the items, use setTimeout to give the UI thread time to update.
setTimeout(function () {
    if (items.length === 0) {
        finish();
    }
    else {
        heavyProcessing(items.shift());
        setTimeout(arguments.callee, 20); // Recur via arguments.callee
    }
}, 20);

However, there is a more elegant solution: function expressions with a name that is only visible from within their own scope.

var factorialOf5 = (function factorial(n) {
    return n === 0 ? 1 : n * factorial(n - 1);
}(5));
console.log(factorialOf5); // 120
console.log(factorial); // ReferenceError: factorial is not defined

To understand why this works, it is important to understand that Javascript has two different forms for creating functions which (perhaps frustratingly) look very similar.

  1. Function declarations introduce a new, named function to the current scope. These are statements by themselves and cannot be combined with other expressions.

    function name( [param*] ) {
        [statement*]
    }

    Note that declarations really perform two operations: one, create a new function; two, bind that new function to name in the current scope.

  2. Function expressions simply create a new function and return it. A function expression can be used as an argument to another function (think callbacks), assigned to variables, or invoked immediately.

    function [name] ( [param*] ) {
        [statement*]
    }

    Name is optional in function expressions, without it the function is anonymous, with it the only the function itself can refer to that name.

In the following example, foo, bar, and arguments.callee all provide a medium to recursion inside the function, however only foo is accessible from outside the function.

var foo = function bar() {
    return foo === bar
        && foo === arguments.callee
        && bar === arguments.callee;
}

foo() // true
bar // ReferenceError: bar is not defined

Not only does using a named function expression provide cleaner code than accessing arguments.callee, it helps you out when it is time to profile or debug your code. A stack trace consisting of ten "anonymous function"s and line numbers is nowhere near as helpful as the named functions will be.

« Previous Entry

Next Entry »

View Comments


Recent Entries


setTimeout Patterns in JavaScript

On August 10th, 2010


ParenScript is an Acceptable Lisp

On August 2nd, 2010


TryParenScript.com

On July 23rd, 2010


In response to "A JavaScript Function Guard"

On July 19th, 2010


My Notes from John Resig's "jQuery Hack Day" Talk

On July 5th, 2010


Announcing Pocco

On June 29th, 2010


Yet Another Lisp

On June 25th, 2010


Recent Happenings: All Play and No Work

On June 21st, 2010


Arguments.callee considered extraneous

On June 2nd, 2010


Javascript, "bind", and "this"

On May 20th, 2010


Creative Commons License

Fork me on GitHub