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

use*_*782 0 javascript scope for-loop function settimeout

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

for(var k = 0; k < 36; k++){
    setTimeout(function(k){ alert(k)}, k*5000);
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

T.J*_*der 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 -------^
}
Run Code Online (Sandbox Code Playgroud)

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

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

但是,那么你将不得不解决一个新问题(这个问题及其答案所解决的问题):k所有这些回调看到的价值都是相同的(36),因为他们看到k的是什么时候的价值它们被调用(后来,一旦循环结束),而不是它们被创建时.

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

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

例:

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

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

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

例:

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

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

for (let k = 0; k < 36; k++){
    setTimeout(() => { alert(k); }, k*5000);
}
Run Code Online (Sandbox Code Playgroud)

...因为当你letfor类似的东西中使用时,它会k为每次迭代创建一个新值,其值不会改变.

例:

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