JavaScript

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.

Arieh Bibliowicz

Arieh is a longtime programmer with more than 10 years of experience in enterprise grade software projects. He has worked as server-size programmer, team leader and system architect in a mission-critical high-availability systems. Arieh is currently a Program Manager (PM) in the Microsoft ILDC R&D center for the Azure Active Directory Application Proxy, and also a PhD student at the Technion where he is developing a Visual Programming Language based on the graphical language of the Object-Process Methodology, using Java and the Eclipse platform.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button