为什么for循环中定义的变量在setTimeout函数中不可见?

时间:2016-08-01 13:14:12

标签: javascript function for-loop scope settimeout

当我在控制台中运行以下代码时:

for(var k = 0; k < 36; k++){
    setTimeout(function(k){ alert(k)}, k*5000);
}

警报显示未定义。此外,我希望在每次迭代后增加超时功能的延迟;但这不会发生。超时功能应该在5秒后首先运行,然后在10秒后再运行15秒,依此类推。

为什么未定义警报,为什么在每次迭代后没有延迟增加?

由于本地超时功能范围内的k,它应该在其中可见。

3 个答案:

答案 0 :(得分:6)

它是undefined,因为定时器机制setTimeout挂钩函数不调用你给它的函数任何参数(默认情况下),并且你已声明k为函数参数。当您使用少于声明的参数调用JavaScript函数时,这些参数的值为undefined。因此参数 k会隐藏(隐藏)循环变量 k,并且您始终会看到undefined

要解决此问题,请不要将k声明为函数参数:

for (var k = 0; k < 36; k++){
    setTimeout(function(){ alert(k)}, k*5000);
    // No k here -------^
}

示例(使用500而不是5000):

for (var k = 0; k < 36; k++){
    setTimeout(function(){ console.log(k)}, k*500);
    // No k here -------^
}

但是,那么您必须解决一个新问题(the one addressed by this question and its answers):所有这些回调看到的k的值都是相同的(36),因为他们看到k的值是在他们被调用时(稍后,一旦循环结束),而不是在他们被创建时。

在ES5及更早版本中,我会这样解决:

function createHandler(k) {
    return function(){ alert(k)};
}
for (var k = 0; k < 36; k++){
    setTimeout(createHandler(k), k*5000);
}

示例:

function createHandler(k) {
    return function(){ console.log(k)};
}
for (var k = 0; k < 36; k++){
    setTimeout(createHandler(k), k*500);
}

...虽然很多人会在循环中重复创建createHandler函数:

for (var k = 0; k < 36; k++){
    setTimeout(function(innerk) {
        return function() { alert(innerk); }
    }(k), k*5000);
}

示例:

for (var k = 0; k < 36; k++){
    setTimeout(function(innerk) {
        return function() { console.log(innerk); }
    }(k), k*500);
}

在ES2015 +(“ES6”及以上版本)中,我会这样解决:

for (let k = 0; k < 36; k++){
    setTimeout(() => { alert(k); }, k*5000);
}

...因为当您在let内使用for时,它会为每次迭代创建一个新的k,其值不会改变。

示例:

// REQUIRES ES2015+ SUPPORT
for (let k = 0; k < 36; k++){
    setTimeout(() => { console.log(k); }, k*500);
}

答案 1 :(得分:2)

您可以通过两种方式传递k参数:

for(var k = 0; k < 36; k++){
    setTimeout(function(k){ alert(k); }, k * 5000, k); // but is not supported in IE9 and earlier
}

或更好地将其包装到函数调用中:

for (var k = 0; k < 36; k++) _setTimeout(k);

function _setTimeout(k) {
    setTimeout(function(){ alert(k); }, k * 5000);
}

答案 2 :(得分:-1)

您将 k 传递给setTimeOut的回调函数,但它不需要任何内容​​。删除参数k将起作用。

for(var k = 0; k < 36; k++){
setTimeout(function(){ alert(k)}, k*5000);
}