简体   繁体   中英

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

When I run the following code in console:

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

The alert shows undefined. Moreover, I expect the delay of timeout function to be increased after every iteration; but that doesn't happen. The timeout function should run first after 5sec then after then 10sec and then 15sec and so on.

Why is undefined alerted and why isn't delay increased after each iteration?

Since k in the local scope of timeout function, it should be visible inside it.

It's undefined because the timer mechanism setTimeout hooks the function to doesn't call the function you give it with any arguments (by default), and you've declared k as the function argument. When you call a JavaScript function with fewer arguments than it declares, the value of those arguments is undefined . So the argument k shadows (hides) the loop variable k , and you always see undefined .

To fix it, don't declare k as a function argument:

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

Example (using 500 instead of 5000):

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

But , then you'll have to fix a new problem ( the one addressed by this question and its answers ): The value of k that all of those callbacks see will be the same (36), because they see k 's value as of when they're called (later, once the loop is over), not when they're created.

In ES5 and earlier, I would solve that like this:

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

Example:

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

...although many would create that createHandler function repeatedly in the loop:

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

Example:

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

In ES2015+ ("ES6" and above), I'd solve it like this:

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

...because when you use let within the for like that, it creates a new k for each iteration with a value that doesn't change.

Example:

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

You can pass the k parameter in 2 ways:

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

or better wrap it into a function call:

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

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

you pass k to the callback function of setTimeOut but it takes nothing.So removing the parameter k will work.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM