Variable Scope Basics

by kirupa   |   20 February 2014

Let's revisit something relating to variables. Each variable you declare has a certain level of visibility that determines when you can actually use it. In human-understandable terms, what this means is simple. Just because you declare a variable doesn't mean that it can be accessed from anywhere in your code. There are some basic things you need to understand, and this whole area of understanding this falls under a topic known as variable scope.

In this tutorial, I'm going to be explaining variable scope by looking at common cases that you've (mostly) already seen. This is a pretty deep topic, but we are just going to scratch the surface here. You'll see variable scope creep up in many subsequent tutorials where we will extend on what you learn here.

Onwards!

Global Scope

We are going to start our exploration of scope at the very top with what is known as global scope. In real life, when we say that something can be heard globally, it means that you can be anywhere in the world and still be able to hear that...something:

the earth

In JavaScript, much the same applies. If we say, for example, a variable is available globally, it means that any code on your page has access to read and modify this variable. The way you make something apply globally is by declaring it in your code completely outside of a function.

To illustrate this, let's take a look at the following example:

<script> 
var counter = 0; 
</script>

Here, I am simply declaring a variable called counter and initializing it to 0. By virtue of this variable being declared directly inside the script tag without being placed inside a function, the counter variable is considered to be global. What this distinction means is that your counter variable can be accessed by any code that lives in your document.

The below code highlights this:

var counter = 0; 

function returnCount() { 
	return counter;
} 

In this example, the counter variable is declared outside of the returnCount function. Despite that, the returnCount function has full access to the counter variable.

At this point, you are probably wondering why I am pointing out what seems very obvious. After all, you've been using global variables all this time without really noticing it. All I am doing here is formally introducing you to a guest that has been hanging around your party for a while.

Note: What Global Really Means

I've been pretty vague in defining what exactly global means. That is deliberate, for formally describing it will involve a whole lot more backstory to make sense of everything. If you are familiar enough with JavaScript (or are feeling adventurous), read on. If not, feel free to skip this note and move on to the next section. We'll revisit this later.

Anyway...something is considered global in JavaScript when this thing is a direct child of your browser's window object. That is a more precise way of saying "declared outside of a function." You can verify this pretty easily.

In our example, counter and window.counter point to exactly the same thing:

alert(window.counter == counter);

Realizing that global variables live under your window object should help you understand why you can access a global variable from anywhere in your document.

 

Local Scope

Now, things get a little interesting when we look at things that aren't globally declared. This is where understanding scope really helps you out. As you saw earlier, a variable declared globally is accessible inside a function:

var counter = 0; 

function returnCount() { 
	return counter;
}

The opposite doesn't hold true. A variable declared inside a function will not work when accessed outside of the function:

function setState() { 
	var state = "on";
} 
setState(); 
 
alert(state) // undefined 

In this example, the state variable is declared inside the setState function, and accessing the state variable outside of that function doesn't work. The reason is that the scope for your state variable is local to the setState function itself. A more generic way of describing this is by saying that your state variable is just local.

Note: Declaring Variables Without Using var!

If I declare the state variable without using the var keyword, the scoping behavior is drastically different:

function setState() { 
	state = "on";
} 
setState(); 

alert(state) // "on" 

In this case, even though your state variable makes its appearance inside the setState function first, not including the var keyword makes this variable live globally.

Any time, a variable that is used without being declared using the var keyword will always live globally.

 

Miscellaneous Scoping Shenanigans

Since we are talking about JavaScript here, things would be too easy if we just left everything with variable scope as they stand now. In the following sections, I am going to highlight some quirks that you need to be famliar with.

JavaScript Does Not Support Block Scoping

Before I attempt to explain this, take a look at the following code:

function checkWeight(weight) {
    if (weight > 5000) {
        var text = "No free shipping for you!";
        alert(text);
    }
    alert(text); // how did it know??!
}

checkWeight(6000);

I've highlighted the relevant lines that you should focus on. Inside the if statement, we declare a variable called text. When this code is run, the alert function directly below it displays No free shipping for you!. That makes sense. What might make less sense is that the second alert that is outside of the if statement also displays No free shipping for you!.

Here is what is going on. Your text variable is declared inside what is known as a block. A block is anything that appears within the open and close brackets - { and }. In many programming languages, variables declared inside a block are part of that block's own scope. That means those variables are local and can't be accessed outside of the block.

JavaScript is not like those many "other" programming languages. JavaScript doesn't support block scoping. For the code you just saw, despite the text variable being declared inside a block, from JavaScript's point of view, it mights as well have been declared at the top of your checkWeight function itself:

function checkWeight(weight) {
    var text = "No free shipping for you!";

    if (weight > 5000) {
        alert(text);
    }
    alert(text);
}

checkWeight(6000);

The behavior is identical in this version compared to what you saw a few moments earlier.

To repeat myself,  there are only two scopes you need to keep track of. The first is the global scope where what you are declaring is completely outside the grips of a function. The second is the local scope where what you are declaring is enclosed by whatever function you are inside.

Note: Meet the let Keyword!

In the near future, the latest version of JavaScript (part of the EcmaScript 6 improvements) will introduce support for the let keyword that allows you to declare variables that are block scoped:

var x = 100;

function theFutureIsNow() {
    if (true) {
        let x = 350;
        alert(x) // 350
    }
    alert(x) // 100;
}
theFutureIsNow();

I'll touch upon some of the improvements being made to JavaScript by the ECMAScript 6 changes at another time.

Correction:
Thanks to Kyle Murray / krilnon for pointing out the EcmaScript 6 angle for
let. See the comments below for more context!

 

How JavaScript Processes Variables

If you thought the earlier block scoping logic was weird, wait till you see this one. Take a look at the following code:

var foo = "Hello!";

function doSomethingClever() {
    alert(foo);

    var foo = "Good Bye!";
    alert(foo);
}

doSomethingClever();

Examine the code in detail. What do you think is shown in the two highlighted alert function calls? Given what is going on, the answer that many people give is Hello! and Good Bye!. What you actually see is undefined and Good Bye!. Let's look at what is going on here.

At the very top, we have our foo variable that is instantiated to Hello!:

var foo = "Hello!";

function doSomethingClever() {
    alert(foo);

    var foo = "Good Bye!";
    alert(foo);
}

doSomethingClever();

Inside the doSomethingClever function, the first thing we have is an alert that should show the value stored by the foo variable. A few lines below that, we re-declare the foo variable with a new value of Good Bye!:

var foo = "Hello!";

function doSomethingClever() {
    alert(foo);

    var foo = "Good Bye!";
    alert(foo);
}

doSomethingClever();

Because our first alert comes before the foo variable re-declaration, the logical assumption is that foo's original value of Hello! will be what gets shown. As you saw earlier, that isn't the case. The value of foo when it hits the first alert is actually undefined. The reason for this has to do with how JavaScript deals with variables.

When JavaScript encounters a function, one of the first things it does is scan the full body of the code for any declared variables. When it encounters them, it initializes them by default with undefined. Because the doSomethingClever function is declaring a variable called foo, before the first alert even hits, an entry for foo is created with a value of undefined. Eventually, when the code hits var foo = "Good Bye!", the value of foo is properly initialized. That doesn't help our first alert function, but that does help the second one that follows the re-declaration of foo.

Keep this little quirk in mind if you ever run into a situation where you are re-declaring variables into a local scope like this simple example highlighted.

Closures

No conversation about variable scope can be wrapped up without discussing closures. That is, until right now. I am not going to explain closures here, for it is a slightly more advanced topic that we will cover separately in the Closures in JavaScript tutorial.

Conclusion

Well, that concludes this topic of variable scopes. This topic seems very simple on the surface, but as you can see, there are some unique characteristics that takes some time and practice to fully understand.

Getting Help

If you have questions, need some assistance on this topic, or just want to chat - post in the comments below or drop by our friendly forums (where you have a lot more formatting options) and post your question. There are a lot of knowledgeable and witty people who would be happy to help you out

Share

Did you enjoy reading this and found it useful? If so, please share it with your friends:

If you didn't like it, I always like to hear how I can do better next time. Please feel free to contact me directly via e-mail, facebook, or twitter.

Kirupa Chinnathambi
I like to talk a lot - A WHOLE LOT. When I'm not talking, I've been known to write the occasional English word. You can learn more about me by going here.

Add Your Comment (or post on the Forums)

blog comments powered by Disqus

Awesome and high-performance web hosting!
BACK TO TOP
new books - yay!!!