tldr;
Creating variables in JavaScript is one of the most basic parts of the language. You probably do it all the time without even thinking twice about it. But if you truly understand how variables are scoped it can save you some issues in the long run, especially when you’re getting started out. In this article, we’ll cover all the basics with variable scope. We’ll cover global, function, and block scoping, as well as the difference in scope when using var
, let
, and const
.
Scope
So, what is scope? Scope is the concept of where you can use a variable or a function in a JavaScript application. Each time you create a variable or function, it has a scope that determines where it can be used. There are three types of scope: global, function, and block. We’ll talk about each these in depth.
Global Scope
The first scope we’ll talk about is global scope. If you declare a variable inside a JavaScript file but not inside a function, that variable will have global scope. Let’s look at an example below:
// index.js
var game = 'Super Mario Brothers';
function play() {
console.log(`You are playing ${game}.`); // You are playing Super Mario Brothers
}
The game
variable is available inside the play
function because it has global scope. It will be available anywhere inside the index.js
file because of where it was declared. Now, this can be desirable, but it is important to be careful when doing this. It’s one thing to have packages that you’re using and have imported be set at a global scope (because you wouldn’t want to reuse the variable identifier for those packages), but it’s a whole different thing to have a variable (such as game
) that could reasonably be reused in different functions at global scope. My advice: be careful and intentional when you declare a variable at global scope.
Function Scope
At first glance, function scope looks very similar to global scope. The difference is pretty obvious though once you see it: variables will only be accessible in the function in which they’re declared or any nested functions. Let’s look at an example:
// index.js
function play() {
var game = "Luigi's Mansion 3";
function format() {
return `You are playing ${game}`;
}
return format();
}
play(); // You are playing Luigi's Mansion 3
console.log(game); // Reference Error
In this case, the game
variable is accessible inside the play
and format
functions, but not outside them. This is less error prone than global scope, because you can reuse common identifiers in multiple functions without the worry of overriding the value of a variable or something like that. My advice: when at all possible, select function scope over global scope.
Hoisting
Okay, before we talk about block scope, it’s important to talk about what hoisting is and what it means when using JavaScript. Let’s take a look at our last example:
// index.js
function play() {
var game = "Luigi's Mansion 3";
function format() {
return `You are playing ${game}`;
}
return format();
}
We have our play
function again, with a variable of game
declared. Under the covers, the JavaScript really looks like this:
// index.js
function play() {
var game = undefined;
game = "Luigi's Mansion 3";
function format() {
return `You are playing ${game}`;
}
return format();
}
So JavaScript takes the var
variable declarations and moves them to the top of the scope where they’re defined, and initializes them to undefined. Then, wherever you had initialized the value, the value is set for that variable. If we were to use console.log
to log the value of the game
variable before it’s intialized by us, the value that is logged would be undefined
:
// index.js
function play() {
console.log(game); // undefined
var game = "Luigi's Mansion 3";
function format() {
return `You are playing ${game}`;
}
return format();
}
and that’s because the function really looks like this when the file is interpreted:
// index.js
function play() {
var game = undefined;
console.log(game); // undefined
game = "Luigi's Mansion 3";
function format() {
return `You are playing ${game}`;
}
return format();
}
This isn’t too bad if you understand what you are doing, but you can get yourself in trouble if you try to use variables before you’ve initialized them. Because no error will be thrown, your function(s) will appear to run successfully but the value of the variable will be undefined
instead of what you may be expecting.
Now that we understand what hoisting is, let’s talk about block scope.
Block Scope
Block scope is similar to function scope, except that any block of code defined by {}
will have its own scoped variables. Variables that have block scoped are created using let
or const
. There are a couple big differences between block scoped and function scoped variables. The first is that if you try to use a block scoped variable in its block but before it’s declared, you will not get undefined
, you’ll get a ReferenceError
error. This is actually good in my opinion, because why would you want to use a variable before declaring it? The second part is that a variable that is declared in a for
loop or if
statement inside your function will not be accessible outside it. Let’s look at quick example first using function scope to show this:
// index.js
function play(numberOfPlayers) {
if (numberOfPlayers === 1) {
var game = 'Super Mario Odyssey';
} else {
var game = 'Super Smash Brothers';
}
function format() {
return `You are playing ${game}`;
}
return format();
}
console.log(play(1)); // You are playing Super Mario Odyssey
console.log(play(2)); // You are playing Super Smash Brothers
Because the game
variable is function scoped, it is still accessible inside the function even though it was declared and initialized inside an if
block. Block scoped variables would not work if we tried the above. The result would be the following:
// index.js
function play(numberOfPlayers) {
if (numberOfPlayers === 1) {
let game = 'Super Mario Odyssey';
} else {
let game = 'Super Smash Brothers';
}
function format() {
return `You are playing ${game}`;
}
return format();
}
console.log(play(1)); // ReferenceError game is not defined
console.log(play(2));
In this case, the format
function can not use the game
variable because it is not available in the play
function or its nested functions as it is block scoped. To fix the issue, we would have to do something like:
// index.js
function play(numberOfPlayers) {
let game;
if (numberOfPlayers === 1) {
game = 'Super Mario Odyssey';
} else {
game = 'Super Smash Brothers';
}
function format() {
return `You are playing ${game}`;
}
return format();
}
console.log(play(1)); // You are playing Super Mario Odyssey
console.log(play(2)); // You are playing Super Smash Brothers
This time, the let
variable is block scoped to the play
function and thus is available in all nested blocks. Variables defined with const
work the same way, except they need to be initialized at the same time they are declared. We won’t be covering that in this article, but you can find more on that by searching for articles that talk about the difference in let
, var
, and const
. My advice: block scope variables whenever possible. It is more restricting than function scope, and should keep your code free of overwriting variables or accessing them outside their if
statement or for
loop.
Conclusion
Hopefully by now you understand the difference in the three types of scope and in the differences between var
and let
/const
. It’s good to know these things as you write JavaScript code so that you understand where variables are scoped to and where they can be used. In my opinion, you should use block scoped variables wherever possible. I personally use const
every time unless I know that I have to overwrite the variable and I have a good reason to overwrite it. But as far as scoping goes let
and const
will give you the same benefit.
Keep an eye out for more JavaScript Fundamentals posts. As I create my Thinkster.io course on JavaScript fundamentals, I’ll be writing a lot of it down here so I can make sure I have it written out in a clear way to explain it to others. Also, go check out my courses on Thinkster.io and keep an eye out for this one on JavaScript Fundamentals. The other two I have are on Deploying Apps to Netlify and Angular CLI Basics
Top comments (0)