I’m having difficulty understanding variable shadowing in JavaScript based on scopes. Consider this small code fragment:
var k = {
prop1: 'test',
prop2: 'anotherTest'
}
for(var k = 0; k < 10; k++) {
console.log(k);
}
//prints number
console.log(typeof k);
//prints 10
console.log(k);
//undefined
console.log(k.prop1);
This is fine, because owing to the immediate function scope, the loop counter variable k shadows the json variable k we declated earlier. Hence the json variable k becomes inaccessible so to speak.
Question:
- In terms of memory allocation, now that there is no way to access
the original json var k, is it eligible for garbage collection? Will
the allocated memory be freed? Or the ‘reference-orphaned’ variable
still live on? If yes, why and for how long? - Is there a way of accessing the original json var k WITHOUT writing
any code before the for loop?
Now consider another slightly modified code fragment:
var k = {
prop1: 'test',
prop2: 'anotherTest'
}
var m = {
prop1: k
}
for(var k = 0; k < 11; k++) {
console.log(k);
}
//prints number
console.log(typeof k);
//prints 10
console.log(k);
//undefined
console.log(k.prop1);
//reference altered? No, this reference points to the original json k
//firebug dumps object to console
console.log(m.prop1);
Question:
- This time, we hold a reference to the original k before hand, in
another json object. And surely, the memory will not be
de-allocated. But, wouldn’t evaluating m.prop1 resolve to an updated
integer k, with a value of 10? Why isn’t this resolution leading to
the loop counter with value 10?
There is only one variable called
k.vardoes not “declare a variable”1 in the sense of other languages. There is no shadowing here. Rather, it is an annotation lifted to the top of the function.The object that was previously known by
kis no longer strongly reachable and thus can be reclaimed. (Exactly when is implementation dependent, but it is eligible.)There is only one variable called
k.vardoes not “declare a variable”1 in the sense of other languages. There is no shadowing here. Rather, it is an annotation lifted to the top of the function.The assignment in the loop overwrites the same
kvariable.Variables are not objects. Expressions, including variable names, are eagerly evaluated in JavaScript. The object named by the variable
kwhenm = { prop1: k }was evaluated is now named bym.prop1. Assigning a new value to the variablekthus has no effect to whatkpreviously evaluated to.The only references to variables in JavaScript appear on the left-hand-side of an assignment or to operators like
typeofordel. Variables are never references in an expression production otherwise. Do not confuse references with Call-By-Object-Sharing, or “object mutating”, semantics: changing a property of an object mutates that object. (As seen, some types likenumberare immutable and do not allow custom properties to “stick”.)The above assumes the the code appears in a function. The rules for
varare slightly different when outside of any function — in that case it does not declare a local variable, ratherkwould just (still) refer to the globalwindow.kproperty.1 The correct terminology is “declares”; however, I find that thinking of it as an annotation, as it is a function-wide attribute about
xand is not “evaluated” in the sense of a statement, is more clear. For example, both of these functions are equivalent:See also:
JavaScript Scoping and Hoisting
Difference between using var and not using var in JavaScript (the answer by kangax)