“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".]

Comments are closed.