MasNotsram
MasNotsram

Reputation: 2273

Javascript closure not using correct value

My knowledge of closures is far from at an advanced level, but it is my understanding that the anonymous function defined, when referencing a local variable defined within the same scope of that function, holds the value of that variable, despite what may affect said variable later.

This would leave me to believe, that in this code, the value alerted when each form field triggers it's onblur should be different (goHandle and go2Handle respectively):

 var formBean = {
                "formString": "Demo",
                "formFields":[
                    {
                        "name":"go",
                        "id":"go",
                        "validationString":"myTest"
                    },
                    {
                        "name":"go2",
                        "id":"go2",
                        "validationString":"myTest2"
                    }
                ]
 };

window.onload = function()
{

    for(var i=0;i<formBean.formFields.length;i++) {
        var field = formBean.formFields[i];
        var fieldMethod = field.name + "Handle";

        document.getElementById(field.id).onblur = function() {
            alert(fieldMethod);
        };        

    }

}

        <input type="text" id="go" />
        <input type="text" id="go2" />

However, what happens is, whichever field you leave, thus triggering the onblur, the second value alerts, suggestion the closure is not a closure at all, but it simply using the current value of the variable.

You can observe this behavior in this fiddle:

http://jsfiddle.net/HpZ39/1/

Could someone please explain what I've done wrong or misunderstood about closures? And why this isn't working the way I expect. Thank-you very much.

Upvotes: 0

Views: 101

Answers (1)

Jon
Jon

Reputation: 437336

This is because you create closure in a loop, while JS variables have function scope.

The variable fieldMethod's scope in reality extends outside the for loop, as if you had declared it at the top of the function (this is called hoisting). Therefore when you create closures for your event handlers all of them bind to the same variable. When the handlers are invoked that variable evaluates to whatever it was during the last loop iteration -- for all handlers.

The solution: since the problem is variable scope, and since a new scope is only created for a function, insert an extra function:

window.onload = function()
{
    for(var i=0;i<formBean.formFields.length;i++) {
        var field = formBean.formFields[i];
        var fieldMethod = field.name + "Handle";

        // Defining an anon function and calling it on the spot, passing the
        // current value of fieldMethod. This results in each handler creating
        // closure to a different variable. 
        (function(methodName) {
            document.getElementById(field.id).onblur = function() {
                alert(methodName);
            };        
        })(fieldMethod);
    }
}

Updated fiddle.

Note that we could have used fieldMethod instead of methodName as the name of the intermediate function's parameter just fine (this would hide the fieldMethod from the outer scope), but I used another name to avoid creating confusion.

Upvotes: 2

Related Questions