Binding listeners inside of a for loop : variable scope miscomprehension

自闭症网瘾萝莉.ら 提交于 2019-12-11 10:37:20

问题


I've a variable scope problem and I don't understand why this occurs and how to get rid of it :

    var items = ['foo', 'bar'];
    for (var index in items) {
        var item = items[index];
        var selector = '.'+item+'-class';
        $(selector).bind('click', function() {
            console.log("class: "+$(this).attr('class'));
            console.log("selector: "+selector);
            console.log("item: "+item);
        });
    }

Considers that this code execute itself over the following HTML :

<div class="foo-class">Foo</div>
<div class="bar-class">Bar</div>

Clicking on "Foo" echoes the right class (i.e. "foo-class") in the first line but the selector and the item name following are related to bar. I think that the problem is that the second iteration of the loop reset the variables used in the first one.

I thought that the declaration inside of the loop should clearly declare their scope at this level. Am I wrong ? Why ? How can I fix it ?

I'm not seeking a workaround, I want something clean and a better comprehension of javascript variable scope mecanism.

Here the jsfiddle.

Thanks !


回答1:


Here's your fiddle example updated.

var items = ['foo', 'bar'];
for (var index in items) {
    (function() {
        var item = items[index]; 
        var selector = '.' + item + '-class';
        $(selector).bind('click', function() {
            console.log("class: " + $(this).attr('class'));
            console.log("selector: " + selector);
            console.log("item: " + item);
        });
    })();
}​

Creating an anonymous function will define a new scope for each of your defined variables

TIP: Try to create a separate function to do the bind, just to keep your code cleaner.




回答2:


It's always the same with these for-loops (google it). JavaScript does not have block scope but function scope, so when an item is clicked the one variable selector has the value it had after the last loop run (same for the variable item).

To solve the problem, you need another closure in you loop which stores the variables in its own scope. That means you need to execute a function for each loop run.




回答3:


The issue is not strictly about variable scope. The anonymous function runs at the time the click event is triggered, not when you are defining it in the loop. Consider the following which is functionally identical to your example:

var items = ['foo', 'bar'];

for (var index in items) {
    var item = items[index];
    var selector = '.'+item+'-class';
    $(selector).bind( 'click', test );
}
​
function test() {
    console.log("selector: "+selector);
}

This (hopefully) demonstrates what's happening: the global variable selector in the function is, at the time the function is being called, the same in both cases ("bar").




回答4:


var items = ['foo', 'bar'];
for (var index in items) {
    (function(i){  
    var item = items[i];
    var selector = '.'+item+'-class';
    $(selector).bind('click', function() {
            console.log("class: "+$(this).attr('class'));
            console.log("selector: "+selector);
            console.log("item: "+item);
        });
    })(index);
}

Fiddle here.




回答5:


Vars "selector" and "item" are references to a location where you store values, and the values of those two at the moment you click one of the htl elements is the one frome the last loop.



来源:https://stackoverflow.com/questions/10131647/binding-listeners-inside-of-a-for-loop-variable-scope-miscomprehension

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!