0

Possible Duplicate:
Javascript: closure of loop?

I'm having some issues with javascript closures that I can't wrap my head around... I usually find them really useful, but this time they're getting in my way.

My problem

I have a list of objects containing some information - let's call it infos. Each object in infos has a string property text, and a property control which references a control object. The control object has (among other things), a function property activate. An example of an object in infos could look like this:

var info = { 
    text: 'Some string', 
    control: { 
        activate: function() { 
            alert('activated!'); 
        }
    }
}

Now, I want to loop through infos and create some new objects (actually, a DOM element, using jQuery) based on the information in the info object.

I've tried something like this:

for (var i in infos) {
    $('<a>')
        .attr('href', '#')
        .text(infos[i].text)
        .click(function() {
            infos[i].control.activate(); // This is where I get problems
        });
}

Now, when executing the click handler, something (in the example above usually the variable i) is undefined, which causes an exception.

I have also tried the following variant:

for (var i in infos) {
    var ctrl = infos[i].control;
    $('<a>')
        .attr('href', '#')
        .text(infos[i].text)
        .click(function() {
            ctrl.activate(); // This is where I get problems
        });
}

In this case I don't get any exception, but regardless of which item I click, it is always the last control that is activated.

My understanding of this, so far

If I've understood javascript closures correctly, this is because when the click handler function (in the second code snippet) is created with a closure that has pointers to the variables defined at the point of defining this function - infos and i in the first example, in addition to ctrl in the second.

I think I could be able to resolve this, if I was able to "avoid the closure"; in other words, I'd like to refer the variables with their values as they were at the point of declaring the click handler function, rather than at the point of its execution.

Is there any way to achieve this? Or, perhaps, an even better solution to this problem?

Community
  • 1
  • 1
Tomas Aschan
  • 58,548
  • 56
  • 243
  • 402
  • Also, please note that the control is not declared at the point of defining the `info` object, so I can't define an `activate()` method directly on the `info` object itself. It would give me exactly the same problems. – Tomas Aschan Jul 24 '12 at 21:46
  • This is a common question. You need an *additional* closure, used inside the loop. Use this code: `$.each(infos, function(i,value){ ...logic... });` – Rob W Jul 24 '12 at 21:47
  • @RobW: My (quite quick) searches here on SO and on Google did not give me anything of use. Would you care to expand on how to do that, or link me to a possible duplicate on SO? That would be most helpful =) – Tomas Aschan Jul 24 '12 at 21:48
  • There we go. On a side-note, you should not use `for(var i in arr)` to iterate over an array. Use a proper `for(var i = 0; i < arr.length; i++)` loop for it! – ThiefMaster Jul 24 '12 at 21:49
  • @TomasLycken I had edited my comment to include the solution for your specific case. – Rob W Jul 24 '12 at 21:50
  • @RobW: Thanks a lot! I'll try that out at once! – Tomas Aschan Jul 24 '12 at 21:56
  • @ThiefMaster: I've never heard about for-in loops over arrays this being an antipattern. Why is it bad? Is there any further reason than the fact that it might be confusing to a future reader (as outlined at the bottom of [this post](http://jaysoo.ca/2010/05/06/javascript-anti-patterns/))? – Tomas Aschan Jul 24 '12 at 21:59
  • One reason is that there is no guaranteed order for property enumeration. Another one is that it breaks when someone extends `Array.prototype` – ThiefMaster Jul 24 '12 at 22:12
  • @ThiefMaster: I don't think the first argument is valid for the case "for-in loops *over arrays*", but I definitely agree with the second. I'll start using `$.each()` from now on =) – Tomas Aschan Jul 24 '12 at 23:29

0 Answers0