Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 390
Carlos Ouro Avatar asked Apr 28 '10 15:04

Carlos Ouro


3 Answers

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);
}
like image 152
Tim Down Avatar answered Nov 11 '22 04:11

Tim Down


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);
  })();
} 
like image 2
John Fisher Avatar answered Nov 11 '22 02:11

John Fisher


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.

like image 1
airportyh Avatar answered Nov 11 '22 02:11

airportyh