0

Here is a code:

var data = [ 'data1', 'data2', 'data3' ];

for (var i = 0; i < data.length; i++) {
    var x = data[i];
    setTimeout(function() {
        console.log(x);
    }, i * 100);
}

The output is 3 times 'data3'. My question is why?

I understand that all 3 log methods will be called after the loop is finished already and variable i will be equal to 3, BUT!

I'm defining x variable inside the loop. I'm expecting on every loop iteration this variable to be recreated locally so that when every of console.log(x) methods are called they refer to 3 different variables stored in the memory.

But it looks like code:

var x = data[i];

updates the same variable instead of recreating it.

Can someone explain this behaviour please?

RhinoLarva
  • 735
  • 10
  • 16

1 Answers1

2

Variables declared using var have function scope. That means there is only one variable in the entire function with that name. It does not matter where the var declaration is. The function declaration is implicitly "hoisted" to the top of the function and is a single variable available to the entire function.

Your code is equivalent to this which shows why there is only one instance of x that is shared by all the setTimeout() calls:

var data = [ 'data1', 'data2', 'data3' ];
var x;

for (var i = 0; i < data.length; i++) {
    x = data[i];
    setTimeout(function() {
        console.log(x);
    }, i * 100);
}

You can solve your issue by introducing a function at the appropriate location in order to uniquely capture each value of x in its own function scope:

var data = [ 'data1', 'data2', 'data3' ];

for (var i = 0; i < data.length; i++) {
    (function(x) {
        setTimeout(function() {
            console.log(x);
        }, i * 100);
     )}(data[i]);
}

In the newest ES6, variables can be declared using let and then they will have block scope as you appear to be wanting. So, in the newest browsers or in node.js, you can use let and it will have the desired block scope:

var data = [ 'data1', 'data2', 'data3' ];

for (var i = 0; i < data.length; i++) {
    let x = data[i];
    setTimeout(function() {
        console.log(x);
    }, i * 100);
}
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Yes. But the variable x is created inside the loop. It's not created in the outer scope, so that loop could change it. Does it mean, that there is no difference in defining x variable inside the loop and outside the loop? It will anyway belong for the outer scope? – RhinoLarva Dec 09 '15 at 23:43
  • 1
    @RhinoLarva - No, that's not how Javascript works. The variable is created at the top of the function regardless of where you locate the `var` definition. Javascript "hoists" all `var` definitions to the top of the function. The variable is ONLY assigned in the `for` loop. It is created at the top of your function. See my first code block for what really happens inside the Javascript interpret. Google for "Javascript variable hoisting" if you want to read more about that. – jfriend00 Dec 09 '15 at 23:44
  • Yea, I have an idea about what hoisting is and how it works. The problem was at I thought that variables declared within the loop are only available inside the loop and are recreated on each iteration. Thank you for your answer, especially for the "equivalent" code snippet. It makes sense now. – RhinoLarva Dec 09 '15 at 23:48