JavaScript Function Tricks for Making Callbacks Better

August 6, 2009

Have you ever needed to pass an extra parameter to a library callback? Perhaps you were confused the first time your method callback was invoked but didn’t have the right this pointer. Someone recently e-mailed me asking how to do this in Strophe, and I figured it was time to finally write up the techniques I use to solve these problems.

In these days of ever-larger JavaScript applications, callbacks have become extremely common and important. Unfortunately, JavaScript doesn’t have bound methods like Python and not every JavaScript library allows passing arbitrary state to callbacks, so getting the right state in callbacks can be irksome. It doesn’t have to be this way though; JavaScript has plenty of primitives available to build tools that make callbacks easier.

:EXTENDED:

Bound Methods

In JavaScript, if you pass a method to a callback, that object that the method belongs to is lost. The this pointer will be the function object itself, not the actual object that it would be if you called obj.foo(). You can make your own bound methods, though, and some JavaScript implementations, like Firefox, even have this built in as Function.prototype.bind().

The bind() method takes the function object it is called on, and returns a new function object. The new function will invoke the original function with this set to the parameter passed to bind().

For example, to create a Python style bound method, you’d write:

var bound_method = some_obj.some_func.bind(some_obj);

Now when bound_method() is invoked, some_func() will have access to some_obj through the this pointer, exactly as if it had been invoked as some_obj.some_func().

This is extremely useful because your code can store state in the object as normal, and not have to worry about passing it along through the callback chain. When your method is invoked, it will know exactly where to find the state it needs.

Prepending Arguments

A lot of frameworks pass a well known set of parameters to callback functions, but sometimes, you need a little extra piece of information. Wouldn’t it be nice if you could create a callback that prepended some arguments to the libraries normal parameters? You can!

Here’s an example using Strophe:

// non-standard stanza handler which takes an extra argument at the beginning
function on_message(response, message) {
    var m = $msg({to: message.getAttribute('from'), type: 'chat'})
                .c('body').text(response);
    connection.send(m);
}

// register the handler for different cases
connection.addHandler(on_message.prependArg('Hi!'), null, "message");
connection.addHandler(on_message.prependArg('Hola'), null, "message");

</code>

Two <message> stanza handlers are registered for the XMPP connection; the first responds with “Hi!” and the second with “Hola!”. prependArg() is used to add an extra argument at the beginning of on_message()’s argument list.

The code for prependArg() appears below.

Function.prototype.prependArg = function (arg) {
    var func = this;

    return function () {
        var newargs = [arg];
        for (var i = 0; i < arguments.length; i++)
            newargs.push(arguments[i]);
        return func.apply(this, newargs);
    };
};

</code>

Strophe uses both bind() and prependArg() to make sure that XMLHttpRequest events are sent along with the proper state since hte XHR implemenations don’t allow passing along arbitrary data with callbacks. I found found bind() to be extremely useful when trying to build large JavaScript projects, as bound methods can make a lot of tasks much easier.

Please leave a comment if you have a favorite function manipulation trick in JavaScript!

JavaScript Function Tricks for Making Callbacks Better - August 6, 2009 - Jack Moffitt