Many aspects of JavaScript code development are taken for granted, and scope is really no different. Of course, in many cases where minimal code is required, variable scope (or function scope) is a non-issue. But if you’re planning to get into larger application development with JavaScript, then you need to understand at least the basics on scope in JavaScript.
Using some simple code examples, I’m going to run through the basics of scope and try to give beginning to intermediate JavaScript developers a better grasp of this very important concept.
Execution Context
JavaScript programs have what is referred to as the execution context of a variable or function, which defines exactly what data the function or variable has access to. Here is a simple example to illustrate two different contexts in one JavaScript program:
var sport = "baseball"; var player = null; function getPlayer() { if (sport === "baseball") { player = "Evan Longoria"; // (the baseball player) } else { player = "Eva Longoria"; // (the actress) } var player2 = "Derek Jeter"; return player; } getPlayer(); alert(player2);
In the above code, we’re declaring two variables, sport
and player
. Both variables are accessible inside the getPlayer
function, since they reside in the global context. Therefore, based on the sport
variable’s value, we can determine a new value for the player
variable.
But, since the function is, in itself, a different context, the variable player2
is only accessible inside the function, and not in the global context. So, this script runs fine up until the last line, which produces an error because the global context does not recognize the existence of player2
.
Demo 1 – Demonstrating Two Different Contexts
What Exactly is the Global Context?
The global context of every JavaScript program coded for the browser is the window
. This can be demonstrated with some changes to the code we just wrote. We’ll omit the last line, and instead execute the getPlayer
function as a method on the window
object. We’ll also “return” the value of player
and wrap an alert
around the function call, to see the return value. After that, we’ll alert the player
variable as a property of the window
object. Here is the code:
var sport = "baseball"; var player = null; function getPlayer() { if (sport === "baseball") { player = "Evan Longoria"; } else { player = "Eva Longoria"; } var player2 = "Derek Jeter"; return player; } alert(window.getPlayer()); alert(window.player);
So if we omit the reference to the window
object, the code will run exactly the same way, because all code that is not inside a function is in the global context, therefore belonging to the window
object. Here’s a demo page:
Demo 2 – Demonstrating that the Global Context is the Window object
Nested Functions Have Their Own Context
As you’ve probably figured out, we could nest functions inside of our getPlayer
function, and each of those nested functions would behave under the same principles we’ve already discussed. Let’s modify our code to demonstrate this:
var sport = "baseball"; var player = null; function getPlayer() { if (sport === "baseball") { player = "Evan Longoria"; } else { player = "Eva Longoria"; } function getPlayer2() { if (player === "Evan Longoria") { player2 = "Derek Jeter"; } else { player2 = "Teri Hatcher"; } return player2; } return getPlayer2(); } alert(getPlayer()); alert(getPlayer2());
It’s a little complex at first, but the above code is a good demonstration of how nested functions affect scope. First, as done previously, we declare the sport
and player
variables. Inside the getPlayer
function we decide the value of player
. Then we have the new getPlayer2
function, nested inside getPlayer
. This nested function determines the value of player2
based on the value of player
. Finally, the value of player2
is returned. So, when getPlayer
is called in the alert
, we can see the current value of player2
.
Here is a demo page:
Demo 3 – Demonstrating Three Different Contexts
But this code produces an error when it gets to the last line. The last line tries to execute the getPlayer2
function in the global context, but this doesn’t work because the getPlayer2
function is not in the global context. Let’s try to alert the getPlayer2
function right before the return
statement in the getPlayer
function:
var sport = "baseball"; var player = null; function getPlayer() { if (sport === "baseball") { player = "Evan Longoria"; } else { player = "Eva Longoria"; } function getPlayer2() { if (player === "Evan Longoria") { player2 = "Derek Jeter"; } else { player2 = "Teri Hatcher"; } return player2; } alert(getPlayer2()); return getPlayer2(); } alert(getPlayer()); alert(getPlayer2());
The above code will alert the value of player2
twice, then produce the same error as in the previous code. The first alert works because the getPlayer2
function is available inside the getPlayer
function context, but not in the global context.
Here is a demo page, with the new alert message added:
Demo 4 – Demonstrating Three Different Contexts
Details to Take Note of in our Example
Here are some simple facts specifically associated with the above example code, to help you understand what is and isn’t accessible in the 3 different contexts:
- The
player
variable is accessible in all 3 contexts (the global, plus both functions), because it is declared in the global context - The
getPlayer2
function is not accessible in the global context because it is declared inside thegetPlayer
function - The
getPlayer2
function is accessible inside thegetPlayer
function - The
player2
variable is only accessible inside thegetPlayer2
function, therefore is not available in the global context or in thegetPlayer
function
Summary
To summarize, every JavaScript program has at least one execution context (the window object), and every function inside that context will add yet another, separate, execution context. Also, all nested contexts will have access to their container contexts, which is somewhat similar to how the CSS Cascade works — although that’s kind of like comparing apples to oranges. Finally, the global context only has access to variables and functions declared inside the global context. And the same would be true for other “parent” contexts that have “child” contexts — they only have access to variables and functions in their own context and above.
I think variable and function scope in JavaScript is fairly intuitive and easy to grasp once you get your hands dirty and immerse yourself in it. There’s much more I could discuss on this topic, but I think that’s enough for the purposes of this post. Feel free to offer comments on anything you feel is important to consider with regards to scope in JavaScript.
The examples should be more well thought.
# if (sport === “baseball”) {
# player = “Evan Longoria”;
# } else {
# player = “Eva Longoria”;
# }
I know it’s a demonstration code but it’s still better if a demonstration code makes sense. Posting examples like if (true) then return true; else return true. Is just bad coding.
@Jakub:
“Evan Longoria” is an American male baseball player. “Eva Longoria” (notice the N missing), is a female actress. They are two different people. I guess the subtle difference tends to look like an error to those not familiar with U.S. pop culture! :o)
I was just having a JS scope discussion recently and I think these were the 3 main points I used:
Functions have scope – a variable declared within a function is not accessible outside that function (vars in a function are “private”)
A variable defined within a function is accessible to its nested functions.
JavaScript is lexically scoped – functions run in the scope they are defined in, rather than the scope they are executed in.