最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

variables - javascript var declaration within loop - Stack Overflow

programmeradmin2浏览0评论
/*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?

/*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?

Share Improve this question edited Dec 22, 2017 at 6:26 Brock Adams 93.5k23 gold badges240 silver badges304 bronze badges asked Apr 28, 2010 at 15:58 Carlos OuroCarlos Ouro 5656 silver badges16 bronze badges 3
  • 1 I would, also, love to see a detailed explanation of what is happening to no's scope here. – Daniel Bingham Commented Apr 28, 2010 at 16:04
  • 2 @Daniel Bingham - no's scope is global unless Trouts is doing this within a function. (Loops don't set a new scope in js.) – user65663 Commented Apr 28, 2010 at 16:07
  • @fig-gnuton Oh. Well that's a simple explanation for it :D Thanks! – Daniel Bingham Commented Apr 28, 2010 at 16:09
Add a comment  | 

4 Answers 4

Reset to default 13

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);
  })();
} 

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.

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.

发布评论

评论列表(0)

  1. 暂无评论