[Coding] JavaScript and Lexical Scoping (aka, Static Scoping)
Posted by Khatharsis on February 11, 2013
I wanted my next Coding post to be about PHP’s arrays since they’re a little odd if you have a background in Java/C#, but I started re-reading JavaScript: The Good Parts and stumbled on “lexical scoping” in the first chapter. I decided it was a more difficult topic to try to understand and will post about PHP’s arrays later.
To start, I consulted Wikipedia on the topic of scoping. As is often the case, the wording in Wikipedia is a bit dense, so I turned to Google. I haven’t quite ingrained the habit of checking StackOverflow, but that is where I ended up and found multiple resources written in more layman’s terms for easier understanding. So, allow me to compound what I have learned.
Let’s start with, What is a scope?
According to Wikipedia, “a scope is the context within a computer program in which a variable name or other identifier is valid and can be used, or within which a declaration has effect.” In other words, depending on where a variable is used, it may be valid (declared), invalid (not yet declared), and/or have different values in different parts of the program.
The simplest example is to take a function scope. In many object oriented languages (e.g., Java, C#), a variable declared within a function exists only within that function:
private void aMethod() {
int aVar = 0;
System.out.println(aVar); // Prints 0
}
System.out.println(aVar); // Unknown variable
Taken the opposite direction, a variable declared outside of a function can be used within the function:
private int aVar = 0;
private void aMethod() {
System.out.println(aVar); // Prints 0
}
System.out.println(aVar); // Prints 0
But care must be taken with global variables as general practice dislikes their use due to security issues.
Now, let’s go a step further and try to understand, What is lexical scoping?
Lexical scoping is when you can look at the source code and easily see the scope with which a variable is valid. In the examples above, it is clear where aVar can be used. The opposite of lexical scoping is dynamic scoping and employs the concept of a global variable within a running function. Take the following code in JavaScript (adapted from the Wikipedia article):
var x = 1;
function g() {
alert('f: ' + x);
x = 2;
};
function f() {
var x = 3;
g();
};
f();
alert('g: ' + x);
I have a global variable (used for demonstration purposes) x and two functions f() and g(). Both functions change the value of x, but which x?
In lexical scope, the output will be 1 and 2. This should not be a surprise for most programmers. Within f(), the declared variable x is local only to f()‘s scope, that is, the value of x (=3) within f() hides the global x (=1); g() has no notion of f()‘s x value and instead uses the accessible global x. Indeed, if the var is left out of the declaration, then the global x will be changed and the resulting output would be 3 and 2.
In dynamic scope, the output will be 3 and 1. What’s going on here? The local x value is passed down to g(). Essentially, f()‘s x value becomes the new global x for g(), overwriting the actual global variable. Unfortunately, I am not sure what would happen if you added a new print within f() after calling g(), i.e., whether the change bubbles up or not. Given the second output is 1, it seems unlikely.
So, What does this have to do with JavaScript?
JavaScript is a difficult language to grasp for those coming from traditional/modern OOP because things that should work do not and things that should not work do. JavaScript, in addition to being lexically scoped, also allows for first-class objects, meaning, functions can be assigned to variables, used as an argument, returned, and stored in an object. In a JavaScript exercise, the author mentioned “when you’re passing around function values, you’re passing around closures!” A closure is a function and its environment. Strange things can happen with closures in JavaScript because functions can be defined within other functions, something that I haven’t commonly found in OO languages.
Take the following code (adapted from the JavaScript Closures lesson):
function outer(x) {
var count = 0;
var f = function inner() {
return count = count + x;
}
return f;
}
var wrapper = outer(1);
alert(wrapper()); // Prints 1
alert(wrapper()); // Prints 2
alert(wrapper()); // Prints 3
Here is a small example of JavaScript’s foreignness. Within the function outer(), I am assigning an inner() function to a variable f, which gets returned when outer() is called. Then, I assign outer() with an argument of 1 to a variable called wrapper. Since wrapper is set to a function call, I call it like a normal function, but I don’t have to provide a value. In fact, even if I provide it with a value, it will still print 1, 2, 3 back because that is how the variable is defined when declared. (Sidenote: If you attempt to print wrapper like a variable, you’ll end up with the declaration of f.)
So, what is going on here? inner() has access to the argument x passed into outer(). That should not be a surprise as I’ve explained lexical scoping above. It seems like count should be reset each time wrapper() is called, that is, it should print 1, 1, 1. In addition, note that wrapper() can be called with no arguments yet return a valid value. This piece of code is an example of how an inner function contains the value of an outer function even after the outer function has returned. This concept can be used to keep track of an internal state as shown in the subsequent calls. (Sidenote: If count is not explicitly assigned to be count = count + x, then the state is not saved, much as you would expect with failing to assign a variable a new value.)
JavaScript is a strange and confusing language, yet extremely powerful once these concepts are mastered.
More resources (going in order may be beneficial; skipping and returning to the Wikipedia entry may also be helpful for deeper understanding):
–Wikipedia’s Scope entry
–StackOverflow’s Lexical Scope thread
–An intro to JavaScript’s lexical scope and closures
–A more advanced look at JavaScript’s lexical scope and closures
–JavaScript exercise with closures