The name of the concept that’s causing you trouble is closure ("enclosure" in Portuguese, but this term is rarely used), and refers to how functions defined within a "lexical context" (i.e. the body of a function, a block, a source file) access variables defined in this context.
In Javascript, only functions define a new lexical context (other languages have different rules - some even support the concept of closure):
var a = 10; // Mesmo "a" para script1.js, script2.js, etc (efetivamente, uma global)
function f() {
var b = 20; // Um "b" diferente para cada invocação de f
if ( x ) {
var c = 30; // Mesmo "c" dentro e fora do if (i.e. o contexto é "f", não o bloco if)
And each new context created within (inner) of an existing context has access to all variables defined in the "outside" (Outer):
function x(a1) { // "x" tem acesso a "a"
var a2;
function y(b1) { // "y" tem acesso a "a" e "b"
var b2;
function z(c1) { // "z" tem acesso a "a", "b", e "c"
var c2;
It is important to note that it does not matter when the internal function will perform, nor what value the external variables had at the time when the object function was created (in contrast to the function definition, which is at compile/interpretation time). What matters is that both share the same variable, and written on one side will reflect on the readings on the other and vice versa.
In your case, you are creating a new (anonymous) function within the lexical context of external code (another? script body function?), and it shares the variable i
(and not the value of i
). At the moment this function is executed, the loop for
has changed its value several times, taking it to its maximum value (50
) and that’s what the internal function will access. If the external code modified or reused i
for other purposes, this would also be reflected in the internal function (and likewise, if one of the function objects i
It would interfere with others too).
There are several ways to modify the code to achieve the desired behavior (i.e. a i
different for each object-function) - as already pointed out by @Sergio - but my favorite is the one that makes the nature of closure more explicit (though it looks visually "strange" to anyone unfamiliar with the concept):
for (var i = 0; i < 50; i++) {
(function(i) {
setTimeout(function() {
textArea.value += 'Mensagem ' + i + '\n';
textArea.scrollTop = textArea.scrollHeight;
}, 100 * i);
})(i);
}
Note that the i
argument of anonymous function is not the same i
passed as parameter to the same - since they are in different lexical contexts. It is also worth noting that the variable textArea
is still coming from the external context, and depending on the case it may be interesting to include it in the closure also:
(function(i, textArea) { ... })(i, textArea);
This ensures that - even if this variable has its value changed (i.e. it points to a different element) - the internal function still has access to the value it had at the time the loop was executed.
Thank you, Segio. The first solution seems to be the cleanest, although I have to write more code. The second example was one I had thought about slightly different. The third seems to have problems with EI 8 and earlier.
– utluiz
Type answer "buy 1 light 3".
– utluiz
@utluiz, :) - Yes, I agree that the first is the best. In the first solution it may be better to use another variable name as a parameter of the function, in case there is some i closed in the same scope. I corrected the answer now too.
– Sergio