【问题标题】:JSHint: Don't make functions within a loopJSHint:不要在循环中创建函数
【发布时间】:2014-09-01 19:19:19
【问题描述】:

在这种情况下解决 JSHint 错误的正确方法是什么?删除function(i) 会解决它吗?这样做会影响性能吗?

for (var i = 0; i + 1 <= pinlatlong.length; i++) {  
    (function(i) {
        setTimeout(function() {
            var latlong_array = pinlatlong[i].lat_long.split(','),
                    marker = new google.maps.Marker({
                    position: new google.maps.LatLng(latlong_array[0],latlong_array[1]),
                    map: map,
                    animation: google.maps.Animation.DROP,
                    icon: pinimage,
                    optimized: false
            });

            // info windows 
            var infowindow = new google.maps.InfoWindow({
                content: pinlatlong[i].title,
                maxWidth: 300
            });

            infoWindows.push(infowindow);

            google.maps.event.addListener(marker, 'click', (function(marker, i) {
                return function() {
                    infoWindows[i].open(map, this);
                };
            })(marker, i));

        }, i * 250); // end setTimeout
    }(i)); // end auto function
} // end for

【问题讨论】:

  • 我会使用pinlatlong.forEach;如有必要,使用 polyfill。
  • Search for messages之前询问。
  • 我个人认为“提示”通常最好忽略,但是有很多重复解释原因和一些解决方案。如果删除了外部 (function (i)) 函数,那么所有 setTimeouts 将使用相同的 i,因为将引入新的 i。在这种情况下,使用 setInterval 然后在内部推进i/the-point ,完成后调用 clearInterval 也可能是谨慎的。
  • @user2864740 提示是因为“外部函数”,即匿名闭包,会在每次循环迭代时重新评估和重新创建。建议不是完全删除该功能。是在循环外定义,所以只创建一次,循环调用就行了。

标签: javascript jslint jshint


【解决方案1】:

如果外部 (function (i)) 函数被移除,那么所有 setTimeouts 将使用 相同 i,因为新的 i(来自函数参数)将 不 被引入(它会lead to problems like this)。因此,不能通过简单地将其从外部匿名函数中删除而不更改其他代码来消除“提示”。 (另见How do JavaScript closures work?

虽然我一般不同意这个“提示”(以及 JSLint 的其他几个建议),但这里有几种不同的方法可以在这种特殊情况下避免“提示”,而不仅仅是禁用/忽略“提示”。


避免警告的一种方法是使用一次setInterval,并且只使用一个回调函数。然后迭代(超过第 i 个/该点)在内部回调内部进行,完成后使用clearInterval。请注意,这里的意图不是“提高性能”或消除“提示”,而是显示一些可能更喜欢或发现更清洁的替代方法。

function addPins (map, pinLatLong, infoWindows) {
    // In separate function so these variables are guaranteed to be
    // in a new function scope.
    var i = 0;
    var timer = setTimeout(function() {
        if (i == pinLatLng.length) {
           clearTimeout(timer);
           timer = null;
           return;
        }

        var latLng = pinLatLong[i]; // only use `i` here

        var latlong_array = latlong.lat_long.split(','),
        // If combining `var` statements, which is another hint I disagree with,
        // consider NOT using a previously introduced var's value immediately as it
        // makes it harder to see the introduction (of latlong_array in this case).
            marker = new google.maps.Marker({
                position: new google.maps.LatLng(latlong_array[0],latlong_array[1]),
                map: map,
                animation: google.maps.Animation.DROP,
                icon: pinimage,
                optimized: false
            });

        // info windows 
        var infowindow = new google.maps.InfoWindow({
            content: latlong.title,
            maxWidth: 300
        });

        infoWindows.push(infowindow);

        // Eliminated use of extra uneeded closure as all the variables
        // used are contained within the callback's function context.
        google.maps.event.addListener(marker, 'click', return function() {
           infoWindow.open(map, this);
        });

        i++;
    }, 250);
}

作为奖励,它避免了我个人在一般情况下不同意的“提示”。来吧,创建数百个函数对象:JavaScript 代码一直在这样做。


第二种方法是使用setTimeout 对附加参数的支持,它将作为回调参数提供。因此,也可以修改代码,而无需额外的function (i) 导致“提示”警告。虽然这确实会产生许多超时(如原始),但它只使用一个函数进行回调并避免额外的闭包。

function showPin (i) {
    // Everything that was in the setTimeout callback
    // ..
}

for (var i = 0; i + 1 <= pinlatlong.length; i++) {  
    setTimeout(showPin, 250, i);
}

另一种方法是通过在另一个函数中创建闭包来进行欺骗,这实际上与原始代码执行相同的操作。可以说这比原版更干净、更容易阅读,即使不是试图消除“提示”本身。

function mkShowPin (i) {
    return function () {
        // Everything that was in the setTimeout callback
        // ..
    }
}

for (var i = 0; i + 1 <= pinlatlong.length; i++) {  
    setTimeout(mkShowPin(i), 250);
}

【讨论】: