This question already has answers here:
JavaScript closure inside loops – simple practical example
                                
                                    (44个回答)
                                
                        
                                3年前关闭。
            
                    
我正在构建一个基于JavaScript的游戏,并在地图上划分了区域。对于每个地区,游戏都会检查某些条件,如果满足,则将单击功能添加到该地区。这将导致以下代码:

for (var ter = 0; ter < territoryStateInfo.length; ter++) {
    for (var adjacent = 0; adjacent < territoryStateInfo[ter].adjacentTer.length; adjacent++) {
        var tempAdjID = territoryStateInfo[ter].adjacentTer[adjacent];
        if (/*long if statement*/) {
            console.log(ter);
            $('#territoryArea' + ter).addClass('viableTarget');
            $('#territoryArea' + ter).click(function(){
                console.log(ter);
                applyNH(ter);
                for (var ter = 0; ter < territoryStateInfo.length; ter++) {
                    $('#territoryArea' + ter).removeClass('viableTarget');
                }
            });
            break;
        }
    }
}


由于某些原因,第一个控制台日志按预期报告变量“ ter”。但是,一旦通过单击相应的区域记录了完全相同的ter,就会返回未定义的值。我的第一个想法是,ter的值将在我单击区域时(在循环结束后且变量不再存在之后)确定,而不是在创建click函数时确定其值。但是,在我看来这不太可能,因为我还有另一段代码可以像这样工作,但是没有给出未定义的值。下面是一个示例:

var viableUtilityTargets = [];
for (var ter = 0; ter < territoryStateInfo.length; ter++) {
    for (var unit = 0; unit < territoryStateInfo[ter].occupiedByUnits.length; unit++) {
        if (/*another long ramble*/) {
            if ($('#territoryArea' + ter).hasClass !== 'viableTarget') {
                $('#territoryArea' + ter).addClass('viableTarget');
                $('#territoryArea' + ter).click(function(){
                    console.log(ter);
                    applyUtility(ter, viableUtilityTargets);
                });
            }
            viableUtilityTargets.push(territoryStateInfo[ter].occupiedByUnits[unit]);
        }
    }
}


有人能弄清楚为什么这个变量按原样运行以及如何解决吗?
先感谢您!

最佳答案

更改此:

$('#territoryArea' + ter).click(function(){
    console.log(ter);
    applyUtility(ter, viableUtilityTargets);
});


对此:

$('#territoryArea' + ter).click(
    (function(ter){
        return function() {
            console.log(ter);
            applyUtility(ter, viableUtilityTargets);
        };
    })(ter)
);


将代码包装在IIFE(立即调用的函数表达式)中将为每次迭代创建新的作用域。外部ter传递给IIFE,以将其设置为内部ter,该内部ter在定义事件侦听器功能的范围内是唯一的。在您的代码中,所有事件侦听器都将引用相同的for,然后使用territoryStateInfo.length循环对其进行递增,直到它变为ter,因此,当发生单击时,事件侦听器内部的将成为长度,无论单击什么按钮=> for循环都会使闭包混乱,因为它在整个迭代过程中都具有相同的作用域,因此,取决于该作用域的所有函数都将引用在该作用域内创建的相同变量(该作用域for循环所在的函数),最糟糕的是,原始作用域中的那些变量一直在变化(即使在循环之后)。

07-24 15:37