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

Why isn't a variable defined in for loop not visible in setTimeout function?

本文关键字:变量 setTimeout 函数 定义 循环 for 为什么不      更新时间:2023-09-26

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

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

警报显示未定义。此外,我预计超时函数的延迟在每次迭代后都会增加;但这并没有发生。超时功能应在5秒后首先运行,然后在10秒后运行,然后再运行15秒,依此类推

为什么未定义会被提醒,为什么每次迭代后延迟都没有增加?

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

它是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 -------^
}

但是,那么您将不得不解决一个新问题(这个问题及其答案所解决的问题):所有回调看到的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);
}

因为当您像那样在for中使用let时,它会为每个迭代创建一个新的k,其值不会更改。

示例:

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

您可以通过两种方式传递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);
}

您将k传递给setTimeOut的回调函数,但它不需要任何东西。因此,删除参数k将起作用。

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