JavaScript Tutorial – Part 3: Variable Scope and Closures
Previous Tutorial: JavaScript Tutorial – Part 2: Variables, Functions, and Objects
Naming is one of the hardest problems in programming. Since there are many things to decide when creating a program, programmers tend to use the same name for variables in many places. And because JavaScript is a very “promiscuous” language, this can cause serious bugs that can be very hard to debug. Therefore it is very important to know what is the scope of the variables we define in our program:
Global Scope
These are variables that you declare outside functions or objects with or without the var
keyword, or variables declared inside functions without the var
keyword. Let’s look at an example:
a = 1; b = 2 var c = 3; console.info("a=" + a + ", b=" + b + ", c=" + c); // prints "a=1, b=2, c=3" function foo() { a = 6; var b = 7 var c = 5; d = 8; console.info("a=" + a + ", b=" + b + ", c=" + c+", d="+d); // prints "a=6, b=7, c=5, d=8" } foo(); console.info("a=" + a + ", b=" + b + ", c=" + c + ", d=" + d); // prints "a=6, b=2, c=3, d=8"
We can see that setting the value of the global variable a
inside function foo
sets the value also outside the function. This can be prevented by using the var
keyword, as done with variable b
. Variable c
behaves as expected as it is declared both globally and locally (a.k.a masking), therefore changes to the variable are only local. Lastly, variable d
is defined inside function foo
without the var
declaration, therefore becomes a new global variable after the function is invoked.
Function Scope
Variables declared (with the var
keyword) inside a function (or an object constructor which is also a function) are scoped to the function and all functions defined inside this function (JavaScript allows us to define functions inside functions, as we will see below). But there are some gotchas that need some investigating. Let’s have an example:
var a = 1; var b = 2; var c; console.info("a=" + a + ", b=" + b+", c="+c); // prints "a=1, b=2, c=undefined" function foo() { var a = 3; var c = 4; console.info("a=" + a + ", b=" + b + ", c=" + c); // prints "a=3, b=undefined, c=4" function bar() { var a = 5; c = 6; console.info("a=" + a + ", b=" + b + ", c=" + c); // prints "a=5, b=7, c=6" } var b = 7; bar(); console.info("a=" + a + ", b=" + b + ", c=" + c); // prints "a=3, b=7, c=6" } foo(); console.info("a=" + a + ", b=" + b + ", c=" + c); // prints "a=1, b=2, c=undefined"
We first define 3 global variables to use in our example. Note that c
has been defined but not yet given a value, so the output of our first print statement show it as “undefined” (yea, very confusing that a defined variable is called “undefined” instead of “uninitialized”… Someone is laughing at us here).
Now comes the interesting stuff. When foo
is invoked, variables a
and c
are redefined, masking the global variables, but when we print their value, we also get b=undefined
. Wat?. What is happening here is that a few lines below we defined b
giving it the value 7
. When a function is called, the JavaScript interpreter scans for all variable definitions inside the function and creates for them a variable that is undefined, and then executes the function. Weird, and definitely something to remember.
Moving forward, function bar
is defined and executed, with a
masking the local a
from foo
, and the assignment of 6
to c
which changes the variable from the closing scope (foo
). At the end of the example, we cab see that all variables in the global scope are unchanged, because we masked them inside the functions.
Closures
A closure is a way to tie a function with variables outside of its scope. The closure of a function contains all of the variables that are not defined inside the function and used by it (and are not global). Since JavaScript allows for the definition of variables inside functions, it is very easy to show how this works:
function foo(x) { var a = x; return function () { a = a + 5; return a; } } var bar1 = foo(10); console.info("a=" + bar1()); // prints "a=15" var = foo(100); console.info("a=" + bar2()); // prints "a=105" console.info("a=" + bar1()); // prints "a=20" console.info("a=" + bar2()); // prints "a=110"
We defined function foo
which returns a function (cool, right?). This internal function uses the value of a
defined in the enclosing scope, creating a closure. When we invoke bar
, the value of a
is already defined an matches the value of the parameter passed to foo
. Furthermore, you can see from the output that each time foo
is called, a new closure is created with a new value of a
.
Awesome.
Reference: | JavaScript Tutorial – Part 3: Variable Scope and Closures from our WCG partner Arieh Bibliowicz at the Vainolo’s Blog blog. |