Understanding var, let, and const

Joseph Haefling
Jan 12 · 8 min read

Whenever you access a variable JavaScript uses something called the scope chain to determine which variable you are referring to.

The scope chain works like this, JavaScript starts looking for the variable in the scope in which it is called, if it can’t find it within that scope it moves up to the next level and looks there, it keeps doing this until it hits the global space, and if it can’t find it in the global scope it throws an error.

I think that the ODB captured this perfectly when he said “You see my name is something that you won’t know unless you’re down with the Brooklyn Zoo”.

Image for post
Image for post
R.I.P. Dirt McGirt

You see, if you are a Wu-Tang Clan fan (aka down with the Brooklyn Zoo) you are aware of the ODB, but if you don’t listen to the Wu, you wouldn’t know that he exists. This sounds pretty simple, right? It can be a little more complex than it sounds, let’s take a look at some code. In the next section, I am going to give you some example code, explain what is going on in the code, and then ask you what the code will return. I put the word result before the answer so that you don’t scroll too far and see the answer before you make a guess.

In this case, we have a variable in the global space called hipHopArtists, and its value is an array of hip hop artists. We also have a function called getDownWithTheZoo, inside of that function we declare another variable called hipHopArtists, and its value is an array of Wu members. getDownWithTheZoo also logs hipHopArtists to the console. In the code we invoke getDownWithTheZoo, and we log hipHopArtists, what do you think that this code will return? Let’s take a look.

Did the code return what you were expecting? So what happened here is that we log hipHopArtists on line 8 and since there is a globally scoped variable called hipHopArtists we get the following array [“Snoop Dogg”, “Mr. Liff”, “Bus Driver”, “Devin the Dude”]. Then, we invoke the getDownWithTheZoo function which also logs hipHopArtists, since there is a functionally scoped variable called hipHopArtists we get the following array [“RZA”, “GZA”, “ODB”, “Inspectah Deck”]. Now, let's make a few changes and see what happens. What do you expect the following code to return?

Did the code return what you were expecting? So what happened here is that we log hipHopArtists and since there is a globally scoped variable called hipHopArtists we get the following array [“Snoop Dogg”, “Mr. Liff”, “Bus Driver”, “Devin the Dude”]. Then, we invoke the getDownWithTheZoo function which logs brooklynZooArtists and hipHopArtists, since there is a functionally scoped variable called brooklynZooArtists we get the following array [“RZA”, “GZA”, “ODB”, “Inspectah Deck”]. Next, we log hipHopArtists and since there is no variable called hipHopArtists in the functional scope JavaScript moves up the scope chain to the global space and there it finds a variable called hipHopArtists and it logs [“Snoop Dogg”, “Mr. Liff”, “Bus Driver”, “Devin the Dude”]. Let’s make a few more changes and see what happens. What do you expect the following code to return?

Did the code return what you were expecting? So what happened here is that we console.log hipHopArtists and since there is a globally scoped variable called hipHopArtists we get the following array [“Snoop Dogg”, “Mr. Liff”, “Bus Driver”, “Devin the Dude”]. Then, we invoke the getDownWithTheZoo function which logs brooklynZooArtists and hipHopArtists, since there is a functionally scoped variable called brooklynZooArtists we get the following array [“RZA”, “GZA”, “ODB”, “Inspectah Deck”]. Next, we log hipHopArtists and since there is no variable called hipHopArtists in the functional scope JavaScript moves up the “scope chain” to the global space and there it finds a variable called hipHopArtists and it logs [“Snoop Dogg”, “Mr. Liff”, “Bus Driver”, “Devin the Dude”]. Then we invoke the getHorror function which has a variable horrorCoreArtists, which logs hipHopArtists. Since there no variable called hipHopArtists inside of the getHorror function, JavaScript moves up the scope chain to the getDownWithTheZoo function, and since there is no variable called hipHopArtists in the getDownWithTheZoo function JavaScript moves up the scope chain to the global space. Low and behold in the global space there is a variable called hipHopArtist and [“Snoop Dogg”, “Mr. Liff”, “Bus Driver”, “Devin the Dude”] is logged to the console. Ok, so now that we know how the scope chain works, let’s take a look at the difference between var, let, and const.

Image for post
Image for post

The first thing that we need to cover when talking about var, let, and const is block scope. So you have probably heard of global scope and functional scope, but what is block scope? Block scope is when you include a variable inside of a code block like a for loop or a conditional statement (if statement). If you take a look at the code below, you will see a variable i that is block scoped inside of the for loop.

Now that we know what block scope is, let’s talk about the differences between var, let, and const. The most important thing to remember about var is that it can not be block scoped. Take a look at the code snippet below, what do you think will be logged to the console?

Did the code log what you were expecting? What happened? So when we run the 1st log we get 11 because i is defined as 11 in the global space. Next, we define i as 0 inside of the for loop, but since var can not be block scoped i leaks out into the global space so now i in the global space has a value of 0. When we run the 2nd log inside of the for loop there is no var in the block scope called i because it leaked out into the global space, so JavaScript moves up the scope chain to the global space and returns i which now has a value of 0. This happens every time that i is incremented until i has a value of 10 and the for loop stops. We then move to the 3rd log which logs i in the global space which has a value of 10 because that is where the for loop stopped. Let’s take a look at what happens when we change the var inside of the for loop to a let.

Notice that the result is very different. That is because let can be block-scoped, meaning that there is a variable i that exists in the global scope and one that exists inside of the for loop. So, the 1st log refers to the i that is defined in the global scope, the 2nd log refers to the i in the block scope which is incremented up to 10, and the 3rd log refers back to the i that exists in the global space. The important take away here is that the let keyword can be globally, functionally, or block-scoped and that var can only be functionally or globally scoped. OK, now that we understand the difference between var and let, let’s discuss const. Take a look at the code below, what do you think will be logged to the console in this instance?

Uh oh, we got an error. Why do you think that is? The const keyword and the let keyword are a lot alike, they can both be globally functionally, and block scoped. There is one very important difference though, the value of a variable declared with const can not be reassigned, so when we try to increment the value of i inside of the for loop we get an error because we can’t change the value of i from 0 to 1. This can be a little confusing because while a variable declared with const can’t be reassigned the value can be mutated. Take a look at the code below to see an example.

Notice that when we add 100 to i and then log it the value of i is still 11. That is because the value of i can’t be reassigned. But, we assign cyclops to the value of an empty array, and then we push the string of I am a mutant, and when we log cyclops we get the following value [ ‘I am a mutant’ ]. That is because cyclops is still the same array, so we didn’t reassign the value of cyclops, we just mutated the array by adding the string of ‘I am a mutant’. I like to think about it like this, if you order a pizza with black olives from your favorite local pizza establishment and they forget to put olives on your pizza, you can take your pizza back and they can add black olives and throw it back in the oven for a few minutes and give it back to you.

So basically they just mutated your pizza, but if you order a pizza with black olives and they accidentally give you wings instead nothing can be done to mutate your wings into a pizza. So they would have to make a brand new pizza.

Image for post
Image for post

Ok, now that we know the difference between var, let, and const we have one more confusing topic to cover in this article. Remember when we talked about the JavaScript Interpreter and I said that during the creation phase the interpreter will go through the code find all of the functions, and variables and save space for them? That process is referred to as hoisting, well let and const are not hoisted by the interpreter. So that means that they are not defined until the interpreter reaches them in the execution phase. Check out the example below what do you think will be logged to the console?

This is where hoisting comes into play. When the JavaScript interpreter enters the creation phase it sees the var keyword and saves space in memory for the variable thing1, it’s not defined yet but there is space set aside for it. Then when it enters the execution phase thing1 is logged to the console and since it isn’t assigned a value until after it is called undefined is logged to the console. Since the let and const keywords are not hoisted, the JavaScript interpreter ignores them during the creation phase.

So when we hit the execution phase and we try to log thing2 to the console we get the following error: ReferenceError: Cannot access thing2 before initialization, because the JavaScript interpreter has no idea that thing2 exists. Hoisting is kind of like going to the record store to buy the newest Wu-Tang album and the record store hasn’t gotten their shipment yet, they know that it exists, they just don’t have it in stock.

Let and const don’t get hoisted, so it’s like going to the record store to buy a Wu-Tang album that hasn’t been created yet, they will tell you that what you are looking for doesn’t exist.

Conclusion

I hope that this article helped to shed some light on scope, the scope chain, and the difference between var, let, and const. These can be pretty confusing topics, as a matter of fact, in writing this article I found that I didn’t understand these concepts as well as I thought. I found that taking some time to tinker with your own code will help to solidify these concepts. I hope that you found this article helpful; if you have any topics that you would like me to cover in the future, drop them in the comments.

JavaScript In Plain English

New JavaScript + Web Development articles every day.

Joseph Haefling

Written by

I am a front-end software engineer, a former social worker, an amateur musician, a foodie, and a recent graduate of Turing School of Software and Design.

JavaScript In Plain English

New JavaScript + Web Development articles every day.

Joseph Haefling

Written by

I am a front-end software engineer, a former social worker, an amateur musician, a foodie, and a recent graduate of Turing School of Software and Design.

JavaScript In Plain English

New JavaScript + Web Development articles every day.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store