Wed 09 June 2010

Quit incorrectly passing functions in Javascript

In my line of work, I edit a lot of legacy code, and with that I see a lot of the following:

// Bad practice. Can you see why?
var foo = function() { alert("bar"); };
setTimeout("foo()", 2000);

That little piece of code is actually a really bad way of doing things, and (to me) is illustrative of when someone is working in Javascript and doesn't actually know what they're doing.

For those who don't know (and would like to learn), setTimeout (and its sister, setInterval) can accept a string of code to be executed as an argument. Know what other method takes a string of code and executes it? eval() does. Generally, methods like these should be avoided, as they leave ways for arbitrary scripts to run if you're not careful.

So how could the above code be improved? Take the following:

var foo = function() { alert("bar"); };

// Better practice. Note how the function is passed this time.
setTimeout(foo, 2000);

What's important to remember here is that *everything* in Javascript is an object. This may seem like grade school material, but it's surprising how many people miss it - since our function (foo) is an object, we can point to it like any other object. What we're essentially doing now is, instead of telling setTimeout "hey, evaluate this script after this timer expires", just "run this function when this timer expires". There's no need to invoke eval() for such a simple operation.

But what about my arguments?

Yes, you can still pass arguments with this form. Whereas before you'd make up a whole string to be executed, you'd now just run it with an anonymous function. For those of you using jQuery, this should feel very familiar:

// Bad practice. Why concoct such an ugly string?
var delayedLol = function(laugh) {
    setTimeout("alert('Laughing: " + laugh + "')", 2000);
};

delayedLol("bwahahaha");

// Instead, pass around your argument (laugh) with anonymous functions.
var delayedLol = function(laugh) {
    setTimeout(function() {
        alert('Laughing: ' + laugh);
    }, 2000);
};

delayedLol("bwahahaha");

Javascript is incredibly flexible. If you're going to work in it, take the time to actually understand what's going on. You (and your users) will be better off for it.

Ryan around the Web