JavaScript Variable Scope [duplicate]

2019-01-08 01:33发布

This question already has an answer here:

I'm having a problem with some JavaScript code.

Script

setTimeout(function() {
    for (var i = 0; i < 5; i++) {
        setTimeout(function() {
            console.log(i);
        }, i * 200);
    }
}, 200);

Outputs

5, 5, 5, 5, 5 instead of 1, 2, 3, 4, 5

I can kind of understand why this doesn't work, but I was wondering if someone could explain to me what's happening and why it's not working!

Also, how can this scope problem be overcome?

5条回答
再贱就再见
2楼-- · 2019-01-08 01:49

Because you are accessing the same variable i in all the functions used in set timeout. The setTimeout function sets the function to fire the number of milliseconds in the future on the same thread as the i variable. The i value isn't copied in the function, the function is referencing the actual variable i when it is fired. Because you have looped through parent function until the i = 5 and this is done before anything else has a chance to fire, they all show up as 5.

查看更多
Fickle 薄情
3楼-- · 2019-01-08 01:52

The variable i exists in the scope of the outer function.

It changes as the loop runs.

The inner function references it.

Try something like this:

var i_print_factory = function (value) {
  return function () {
    console.log(value);
  };
};

var init_timers = function () {
  for (var i = 0; i < 5; i++) {
    setTimeout(i_print_factory(i), i * 200);
  }
};

setTimeout(init_timers, 200);
查看更多
我欲成王,谁敢阻挡
4楼-- · 2019-01-08 01:53

The setTimeout callback functions are executed asynchronously, all the console.log calls you make refer to the same i variable, and at the time they are executed, the for loop has ended and i contains 4.

You could wrap your inner setTimeout call inside a function accepting a parameter in order to store a reference to all the i values that are being iterated, something like this:

setTimeout(function() {
    for (var i = 0; i < 5; i++) {
      (function (j) { // added a closure to store a reference to 'i' values
        setTimeout(function() {
            console.log(j);
        }, j * 200);
      })(i); // automatically call the function and pass the value
    }
}, 200);

Check my answer to the following question for more details:

查看更多
ゆ 、 Hurt°
5楼-- · 2019-01-08 01:55

You're trying to create a closure containing the variable "i". But closures are only created at the end of a function. So if your functions are created in a for loop, they will all have the values from the last iteration.

You can fix it with something like this:

var createFunction = function(index) {
  return function() {
    console.log(index);
  }
};

for (var i = 0; i < 5; i++) {
  setTimeout(createFunction(i), i * 200);
}

where you return the function from another function.

查看更多
走好不送
6楼-- · 2019-01-08 02:06

Take a look at this question. It might help you understand the scope and closures better, very similar to your question.

查看更多
登录 后发表回答