Var, Let, Const — No More Confusion

Rezaul Karim
Geek Culture
Published in
10 min readJun 8, 2021

--

There are so many programmers who claim to love JavaScript, code in JavaScript, eat JavaScript, and sleep with JavaScript. But there is no such programmer who never had to go through a hard time comprehending many of its ambiguous features and their unpredictable behaviors.

The ambiguity around the declaration of JavaScript variables (functions and objects can also be stored in variables) is one that can make a programmer’s life hell if there is a lack of proper understanding of scopes and a variable’s life cycle.

You probably know that var, let, and const are used to declare a variable in JavaScript. To remind you, there is another kind of declaration in javascript, which requires no keyword at all. We refer to such variables as “global”. When we call something global, it is supposed to be accessible anywhere in the code.

There are other variables that we don’t want acting like global ones. We want them to be limited within certain blocks of the code. We refer to such variable as “local”. When we say something like “a certain block of code”, it brings up another core JavaScript feature called “scope” into the play.

But what exactly do we have to do with the scope and global/local variables while understanding variable declaration and the distinction among var, let, and const? Keeping this question unanswered for the time being, we will now jump into the next section. I promise that you yourself will be able to answer the question by the end of this blog.

Let’s deal with Scope first

Like many other programming languages,Scope is a vital feature of javascript that generally refers to a certain block of code. Anything declared in a scope is only accessible from within it’s own world. outside it, those variables just don’t make sense. For example, if we declare anything in a function, we cannot access it outside the function. They are said to be local within their boundary. There are mainly two types of scope in javascript .

  1. Global.
  2. Local.

In javascript , any variable declared without any specific keyword has global scope. For example, “window” is a global object in browser.

For ease of understanding , we could divide the local scope further into two separate scopes.

  1. Function Scope .
  2. Block scope.

Everything inside a function is said to have function scope.That means when the function stops being called, all of it’s variables also get vanished.

function Add(a,b){
var sum = a+b; // sum is a local variable in this function;
return sum;
}
console.log(Add(2,3)) // returns 5 console.log(sum) // throws reference error. as there is no sum outside the function.

But what if we write something like this

function Add(a,b){ 
sum = a+b;
return sum
}
console.log(Add(2,3)); // output : 5
console.log(sum) ; // output : 5

We have seen that , Though we didn’t declare “sum” anywhere outside the function , we could still access and print it. Shouldn’t anything declared inside a function be a “local” variable?

Hold on! we don’t do this here. The game has just started!

There is another scope called block-scope. Function scope is also one kind of block scope, but not every block scope is a function scope. Any loop , if/else conditional block and switch is said to be a block scope. Even if you put anything inside a pair of curly braces , it will have a block scope.

{

let a = 5;
const b = 10;
console.log(a) // returns 5
console.log(b) // returns 10
}
console.log(a) // Reference error, a is not defined.
console.log(b) // Reference error , b is not defined.

We have seen that, a and b are only accessible inside the block scope. So far,so good! To give you a twist, what if we write something like this -

{
var a = 5;
console.log(a)// returns 5
}
console.log(a) // returns 5

We have declared variable “a” inside the block and it should have been limited within it’s own world only.Then how could we manage to print “a” even though we didn’t declare it outside the block?

If you give a closer look at previous two code snippets, you should notice that, we declared a” with “let” in the first example and declared it with “var” in the second.

Now you have probably guessed that there must be something between scope and the declaration with var ,let and const to care about. From now on, we will start seeing the real pictures. Are you ready ?

Global variable

When we declare any variable without a keyword like var/let/const it is considered to have global scope and can be accessed from anywhere in the code. As a result, even if you declare such global variable inside a function or loop, they are not bounded to their own block .They just belong to global window (in case of browser) object. Now go back to the example above where we wanted the global variable “sum” to be limited within it’s function scope and think again why it didn’t happen so.

World of Var

“var” is strictly scoped to the function where it has been declared. I repeat the word “function”. Yes, var is function scoped! So anything we declare with var , should only be accessed from within the function and it should not make any sense outside it. That means, we can still access the var type variables outside the block scopes like “loop”/if-else/or a pair of palin curly braces. This is the reason why we could be able to print such variable outside the block in one of our previous examples. Let’s see it again

{ 
var a = 5;
}
console.log(a) // returns 5;

In this example, Though we didn’t declare variable “a” anywhere outside the block, we could still print it. Remember the rule! anything declared with var is function scoped.In the example, the block within curly braces is different from that of a function. But wait! there is no explicit function either. Actually everything in javascript is considered to be run within the scope of a global object called window [in Nodejs it is called “Global”] .When variable “a” comes to know that there is no explicit function where it has been defined, it just gets attached to the window object as it’s property and starts behaving like a global variable. It doesn’t necessarily root for the fact that any variable declared with “var” should have global scope.A common confusion among beginners. You see why!

Kingdom of let and const

Any variable that is declared with let and const are said to have block scopes. By saying block, function is also included here as well as loop,conditional and block with curly braces .If we try to access any let,const type variable outside it’s block , we can’t do so.

{ 
let name ="reza";
let age = 25;
const nationality = "Bangladeshi"
}
we can't access those variables here.

Actually let and const give us much more predictability and consistency as compared to “var”.Some are mentioned below.

{ 
let x = 5;
let x =7; // it will raise syntax error , as x can't be redeclared within same scope.
}
var name = "reza";
var name = "sakib";
console.log(name) // sakib // name is just redeclared

hoisting comes into play

Both var and the group of let/const actually get hoisted up to the top of respective scope but their declaration mechanism is completely different.We can access any “var” type variable before initializing it in our code (though it returns undefined). lets understand a simple rule regarding hoisting,though hoisting requires a separate blog to be discussed in detail.

  • whenever the code starts running, every variable declaration just gets hoisted up to the top of it’s respective scope. In the very first line, the variable is just introduced to the memory.
  • The next task is to set “undefined” to the variable as initial value. The place really depends on whether we are hoisting a var type variable or that of a let/const type. For the case of var, it is set immediately after the first line . For the case of let/const ,it is set on the line where the variable is actually defined in code.
  • The final task is to assign the actual value to the variable if it was initialized with any . It is done on the line where the variable is actually defined and initialized in main code, for both case of var and let/const.

Let’s understand this with example.

console.log(x) // undefined . not error.
var x=5;

2.

console.log(x) // throws reference error; 
let x=6;

For the ease of understanding we can assume that the first code snippet is converted to something like this behind the scene.

1.just tells the memory to assign a block for x. no value would be set. 
2.x = undefined;
3.console.log(x) // undefined;
4.x = 5

x was hoisted up to the top of the scope. It got introduced to memory and no value had been attached to it so far. In the next line , it was set to “undefined” .The actual value got attached in the line where “x” was actually initialized in main code.

But in case of let and const, we can assume that hoisting and declaration work this way .

1. just tells the memory to assign a block for x. 
no value would be set.
2. console.log(x) // throws reference error. It hasn't been initialized yet. but javascript engine recognizes "x". 3. x= undefined;
4. x= 5;

“let” type variable was hoisted too . But the value “undefined” was set in the line where it was actually defined in main code. As a result, there is no chance to see “undefined” if we console log it before the initialization.The actual value will be assigned immediately after this .

[ Note : Many developers understand hoisting this way even though there is no such mechanism when javascript code is run by the engine. To know the actual fact regarding hoisting , we should know about “Execution Context”. I will talk about this in a later blog very soon ].

We have seen that how let and const give us much more predictable result. This is the reason why Ecmapscript2015 introduced this two game changing players to us. With the help let/const declaration we can also ensure self containing behavior of any block and function in our code. For example :

let a = 5; 
{ let a = 10;
console.log(a) // returns 10
}
console.log(a) // returns 5

This is really predictable, right ? We just decalared “a” at two places. a=5 is in the global scope.We also declared “a” inside a block and expected it to be different from the previous one. The result is fine.

But if we try to imitate something like this with “var” ,let’s see what happens!

var a= 5 ;
{
var a =7;
console.log(a); // returns 7
}
console.log(a) // returns 7

As it is described earlier, variable “a” has function scope. Though we declared “a” at two places and expected them to be different from each other, they both actually belong to the same world/scope . As a result, the declared “a” inside the block is kind of a re-declaration of “a” .In such use cases , this might give us unpredictable result.

A curious mind will surely be affected by one critical question that why i haven’t distinguished “const” from “let” yet.The reason is, i expected you to know the core difference between let and const, which is frank .To shade more light on it

we can assign value to a let type variable multiple times after the declaration.But we cannot do this with “const” as it is expected to be unchangeable by it’s nature.

let a = 5;
a = 7 // this is valid

But,

const a =5;
a =7 // this is not valid. throws syntax error.

There is a gentleman reminder regarding const type declaration with “object”.Let’s see the code first.

const person =
{
name :"reza",
age : 25
}
person.name = "sakib"; // valid;
person.age =28; // valid;

we can actually change the property of a const type object. what we can’t do is, we can’t reuse “person” to refer something once again after it has been defined with const.

Good Practices

  • Don’t mix up “var” and “let” in your code. Stick with one.
  • Though let and const will be converted to “var” in the end (By transcompiler), it is a good practice to use let and const in your code as much as possible.In modern javascript library like React, developers almost never use “var” in their code.
  • Stop creating global variable without any keyword. If you declare anything with var in the main function, it is automatically attached to the window object. You can also declare anything with “let” and “const” in the main scope if you want it to be global. In that case,it will not be attached to the window object, but still show the same global behavior. Moreover, it works in much predictable manner.

In the end, It is important that we take hoisting and scopes into consideration while understanding the inner mechanism of declaring variables with var , let and const. I will talk about hoisting in much more detail in a different blog .This blog is written to give you a foundation on variable declaration in javascript so that you can resolve any kind of confusion around let , var and const. Now challenge yourself with following examples and try to guess output without running the code . If you have troubles regarding understanding them feel free to comment below or reach me via rezaink1996@gmail.com .

[hint : first understand the scope of variables, then apply hoisting rule mentioned in a section above]

1.

var x = 5; 
if(x===5){
let x =7;
}
console.log(x) what will be the output ?

2.

var x = "game";
function demo (){
if(x==="game"){ x="cricket"
}
else if (x==="music"){
x = "rock"
}
else{
x = "javascript";
}
console.log(x)
var x ="music";
} demo() // what will be printed ?

To sum it up, I kept one question unanswered in a section above and promised that you should be able to answer it yourself. Let me know in the comment section if i kept my promise or not .

--

--