Manngo
Manngo

Reputation: 16281

JavaScript closures & callback functions

Can someone clarify this point for me?

Two of my favourite features in JavaScript are closures and the fact that functions are first class objects.

If I want to take advantage of closure in, say, a callback function, then the function must always be an inner function. For example:

doNested();
function doNested() {
    var message='Hello';
    window.setTimeout(inner, 3000);
    function inner() {
        alert(message);
    }
}

There is no way I can pass a parameter to an outer callback function. For example:

doSeparated();
function doSeparated() {
    var message='Goodbye';
    window.setTimeout(outer,3000);
}
function outer() {
    alert(message);
}

This doesn’t work, of course.

The question is, is there any way to pass inner variables to an external function like this, short of adding them to a parameter list?

Thanks

Upvotes: 1

Views: 948

Answers (1)

Aadit M Shah
Aadit M Shah

Reputation: 74204

If I want to take advantage of closure in, say, a callback function, then the function must always be an inner function.

That's correct. Every function in JavaScript only has access those variables which are either defined in its own scope or defined in a parent scope1. Therefore, your first example works while your second example doesn't.

The question is, is there any way to pass inner variables to an external function like this, short of adding them to a parameter list?

No, there's no way to do that. Well, technically you could add your inner variable to an object and then bind that object to the external function after which you can access the inner variable from the this context of the external function, but that is no better than passing the variable to the function directly.

doSeparated();

function doSeparated() {
    var message = "Goodbye";

    setTimeout(outer.bind({
        message: message
    }), 3000);
}

function outer() {
    alert(this.message);
}

Since you are using setTimeout, you can pass extra arguments to setTimeout which will be given to the callback function. This gets rid of the nasty bind:

doSeparated();

function doSeparated() {
    var message = "Goodbye";
    setTimeout(outer, 3000, message);
}

function outer(message) {
    alert(message);
}

Note that both bind and extra arguments of setTimeout don't work in older versions of IE. In that case, you can use currying instead (which in my humble opinion is the best solution aside from your original nested solution):

doSeparated();

function doSeparated() {
    var message = "Goodbye";
    setTimeout(outer(message), 3000);
}

function outer(message) {
    return function () {
        alert(message);
    };
}

Other than these, there's no other good solution that I can think of. The best solution is your original nested solution.


1 A function doesn't have access to any variable defined in a child scope or else you would be able to access every variable from the global scope.

Upvotes: 1

Related Questions