“Fun example of programming language scope” is only “fun” for a certain type of geek; But I like programming examples that help explain how your code is interpreted, particularly if the lesson can help prevent a certain class of bug.
Now that you’re expecting a scope puzzle, what will the following JavaScript print? (Ignoring the line numbers, of course, which are here to aid in discussion.)
1: var foo = 1;
2:
3: function bar1() {
4: print("A: " + foo );
5: }
6:
7: function bar2() {
8: print("B: " + foo );
9: var foo = 2;
10: print("C: " + foo );
11: }
12:
13: function bar3() {
14: print("D: " + foo );
15: eval("var foo = 2;")
16: print("E: " + foo );
17: }
18:
19: bar1();
20: bar2();
21: bar3();
No peeking…
Answer
A: 1 B: undefined C: 2 D: 1 E: 2
“A” is easy. Since foo is a “free variable” (i.e., not defined within function bar1), the interpreter goes up the scope chain, and finds the global foo, defined on line 1.
For bar2, “C: 2″ is obvious — it’s “B: undefined” that lets you in on the magic under the hood. You sort of expect to see “B: 1″ (or a compiler error.) However, JavaScript interpreters scan-ahead, searching for variable definitions (e.g., var statements) when parsing a code block. The interpreter sees/re-writes bar2 like this:
1: function bar2() {
2: var foo;
3: print("B: " + foo );
4: foo = 2;
5: print("C: " + foo );
6: }
With that definition, “B: undefined” makes perfect sense.
To short-circuit the magic, bar3 uses eval() to do it’s trickery. At line 14, foo still points to the global foo, much like in bar1; However, the eval statement on line 15 modifies the local scope, introducing a new, local foo. By line 16, “E: 2″ is using the newly introduced foo.
The lesson: Even though JavaScript allows you to declare variables at any point within a block, putting your var statements at the beginning of the block can help eliminate scope confusion around whether an inner- or outer-closure contains the correct value.
Bonus Question
Is JavaScript’s var a let or let*? It’s easy to find out using the following:
1: var x = 2, y = 3, z = x + y;
2: print(z);
Is this legal? Will it print ’5′?
[Update: 2010/09/21: If you liked this, you'll also enjoy "JavaScript Scoping and Hoisting".]