setTimeout appears to be changing my variables! Why?

The others already wrote the reason for the behaviour you're getting. Now the solution: change the setTimeout line to:

(function(i) {
    setTimeout(function(){console.log(i)}, 1000);
})(i);

This works because it captures the current value of the variable i into yet another closure, and the variable within that closure doesn't change.


ECMAscript uses the technique of lexical closures which is nothing else than an internal storage to all parent context objects / lexical environment records (ES3 / ES5).

In short words, your anonymous function used by setTimeout closes over that i variable, so while that timeout is "waiting", your loop continues. setTimeout operatores asynronously of course and that in turn means, at the time that loop has finished the value if i is of course 2.

Now remember about the closure stuff, our anonymous function in setTimeout still holds a reference to i when it finally fires (after 1000ms). So it correctly shows you the value of 2.

If you want to show the numbers for each iteration after 1000ms, you need to invoke another context. This might look similar to

setTimeout((function( local ) {
    return function() {
        console.log( local );
    };
}( i )), 1000);

The for loop continuously increments i until the loop condition is met, even though the code in the for loop does not execute, when the code in the setTimeout executes it shows the current value of i - which is 2.