I am trying to understand JavaScript scope rules. What I have read in textbooks and the documentation is confusing.
It seems to me that JavaScript is a statically (or lexically) scoped language - when trying to bind a variable name to a variable (definition), the lexical structure of the code is used.
An execution context seems to be similar to a stack frame on the call stack. Each execution context has a variable object upon which all the local variables (of the associated function) are defined. These variable objects are linked together to provide a 'scope chain' from the variable object at the top of the stack to the variable object at the bottom of the stack (the window object). This scope chain is searched from top to bottom in binding variable names to variables. This is very similar to statically scoped languages like C/C++/Java.
There seems to be one important difference with respect to C/C++/Java -- it is possible to access a variable defined in a function whose stack frame is no longer on the call stack, as shown in the example below:
var color = "red";
var printColor;
function changeColor() {
var color = "green";
printColor = function(msg) {
alert(msg + color);
}
printColor("in changeColor context, color = "); // "green"
}
changeColor();
// stack frame for "changeColor" no longer on stack
// but we can access the value of the variable color defined in that function
printColor("in global context, color = "); // "green"
Have I got this right? Are there other issues I should be aware of?
Thanks in advance
This is indeed a major difference between C/C++ and JavaScript: JavaScript is a reference-counted, garbage-collected language, which means that objects can be reclaimed by the engine when they no longer have any references to them. The function you assign to
printColor
isn't on the stack, per se, as it would be in C or C++; it's allocated dynamically and then assigned to a variable outside your current scope. So, when control flow returns fromchangeColor
, the anonymous function still has a reference count of 1 since the outerprintColor
refers to it, and thus it's usable from the outer scope.So, your example isn't so much of a scoping issue--it's clear that you declare
printColor
outside of the function scope ofchangeColor
. When you definechangeColor
, it closes the upvalueprintColor
into the new function scope, making it accessible. Like Combat said, if you add avar
to the second, inner definition ofprintColor
, it'll shadow the firstprintColor
you declared and it won't be accessible outside that function block.As far as other issues to be aware of, yes, there are quite a few, but see my comment on your original post for a good start.
It always comes down to lexical scoping which is function are executed with its scope chain when it is defined, not when it is invoked.
The anonymous function is defined in the local scope of function changeColor instead of the global scope. Hence when it is executed again, it prints out the color green which is listed in the local scope of function changeColor.