简体   繁体   中英

javascript var declaration within loop

/*Test scope problem*/
for(var i=1; i<3; i++){
    //declare variables
    var no = i;
    //verify no
    alert('setting '+no);

    //timeout to recheck 
    setTimeout(function(){
        alert('test '+no);
    }, 500);
}

It alerts "setting 1" and "setting 2" as expected, but after the timeout it outputs "test 2" twice - for some reason the variable "no" is not reset after the first loop...

I've found only an "ugly" workaround:

/*Test scope problem*/
var func=function(no){
    //verify no
    alert('setting '+no);

    //timeout to recheck 
    setTimeout(function(){
        alert('test '+no);
    }, 500);
}
for(var i=1; i<3; i++){
    func(i);
}

Any ideas on how to workaround this problem in a more direct way? or is this the only way?

JavaScript does not have block scope, and variable declarations are hoisted. These facts together mean that your code is equivalent to:

var no;

/*Test scope problem*/
for(var i=1; i<3; i++){
    //declare variables
    no = i;
    //verify no
    alert('setting '+no);

    //timeout to recheck 
    setTimeout(function(){
        alert('test '+no);
    }, 500);
}

By the time your timeout function executes, the loop is long finished, with no retaining its final value of 2.

A way around this would be to pass the current value of no into a function that creates a fresh callback for each call to setTimeout . Creating a new function each time means each setTimeout callback is bound to a different execution context with its own set of variables.

var no;

/*Test scope problem*/
for(var i=1; i<3; i++){
    //declare variables
    no = i;
    //verify no
    alert('setting '+no);

    //timeout to recheck 
    setTimeout( (function(num) {
            return function() {
                alert('test '+num);
            };
        })(no), 500);
}

This is essentially the same as your fix, but using a different syntax to achieve the scoping adjustment.

/*Test scope problem*/
for (var i = 1; i < 3; i++) {
  //declare variables 
  var no = i;
  //verify no 
  alert('setting ' + no);

  //timeout to recheck
  (function() {
    var n = no;
    setTimeout(function() { 
      alert('test ' + n);
    }, 500);
  })();
} 

I'm liking that I can get so much mileage out of this answer .

If you need help applying that answer, let me know.

EDIT

Sure. Let's look at your original code.

//timeout to recheck 
setTimeout(function(){
    alert('test '+no);
}, 500);

See that anonymous function? The one you pass into setTimeout() ? It isn't called until the timer says so - which at 500ms is well after the loop has exited.

What you're hoping for is for no to be evaluated "in place" but its not - it's evaluated at the time the function is called. By that point, no is 2.

In order to get around this, we need a function that executes during the iteration of the loop, which itself will return a function that setTimeout() can use in the way we expect it to.

setTimeout(function( value )
{
  // 'value' is closed by the function below
  return function()
  {
    alert('test ' + value );
  }
}( no ) // Here's the magic
, 500 );

Since we create anonymous function and immediately call it, a new scope has been created in which we can close off variables. And that scope closes around value , not no . Since value receives a new (ahem) value for each loop, each of these lambdas has it's own value - the one we want it to.

So when setTimeout() fires, it's executing the function returned from our closure function.

I hope that explains it.

Javascript does not have lexical scoping(the for-loop does not create a new scope), and your solution is the standard workaround. Another way to write this might be:

[1, 2].forEach(function(no){
    //verify no
    alert('setting '+no);

    //timeout to recheck 
    setTimeout(function(){
        alert('test '+no);
    }, 500);
})

forEach() was introduce in ECMAScript 5 and is present in modern browsers but not IE. You can use my library to emulate it though.

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