Hello folks. Welcome to my another post.
In this post, we will learn about var, let and const from the basics to the magical parts of it. So please bear with me till the end.
Variables
- Variables in programming languages are means to store information in memory and assign a human-readable label to it for future references.
- There are a few rules for variable naming:
- The name must contain only letters, digits, or the symbols $ and _
- The first character must not be a digit
- It can't contain any whitespace characters
- There are some reserved words that can't be used as variable names
- In Javascript, there are two types of variables: Primitives and Reference types. Boolean, string and number are examples of primitive types while objects and arrays are examples of reference type.
- Javascript is a dynamically typed language. That means we can assign different types to different variables without having an error(for
varandletkeywords and notconst) - In Javascript, we can declare a variable using
var,letandconst.
Some of the terms that I will be using in this post
Scope
- Scope in Javascript refers to the variable's accessibility in the code. Based on the scope of a variable, some variables can be accessed in some part of the code while some can't be accessed in that part of the code.
- There are three types of scopes: Global, Function and Block.
- Variables declared at the top level(outside any function) are global scoped. They can be accessed throughout the program.
- Variables declared inside a function are function scoped and can only be accessed inside that function. It will throw a reference error if tried to access outside the function.
- Variables declared inside
{}are called block scoped and their accessibility depends on the keyword that was used to declare them(fromvar,letandconst).
Scope chain
- Javascript creates scopes for every executing function and
{}block. There is also a global scope that holds some special values and variables that are in the global scope. - Each scope has access to the parent scope in which it is defined. By using it, the current scope can access the variables from the parent scope. This creates a chain of scope which is called a scope chain.
Hoisting
- JavaScript Hoisting refers to the process whereby the interpreter appears to move the declaration of functions, variables or classes to the top of their scope, before execution of the code.
I won't be going deep into any of these topics right now. (Maybe in the future posts 😉)
Now let's learn about var, let and const.
var
- The
varkeyword is an old way of creating variables in Javascript. - Javascript engine doesn't throw an error if we try to create two variables of the same name in the same scope using
var. If the second statement is an assignment then it will replace the value within the variable. If the second statement is just a declaration then it will be ignored. Javascript engine won't throw an error here.
var test = "Hello";
var test;
console.log(test); // Hello
var test = "Nice";
console.log(test); // Nice
- The scope of a variable declared with
varis its current execution context and closures(Maybe in the future post). In simpler words,vardeclarations are function scoped and accessible inside that function and variables that are declared in the global scope are accessible anywhere.
function testFn() {
var test1 = "Hello";
if (true) {
var test2 = "Nice";
}
console.log(test1, test2); // Hello Nice
function innerFn() {
var test3 = "Wow";
console.log(test1, test2); // Hello Nice
}
// test3 is not accessible here.
// It will throw a ReferenceError.
}
testFn();
// test1, test2 and test3 are not accessible here.
// They will throw a ReferenceError.
- When using
var, we can modify or re-assign any type of primitive values or reference values.
var test = "Hello";
test = "Nice";
console.log(test); // Nice
test = 5;
console.log(test); // 5
test = ["wow"];
console.log(test); // ["wow"]
-
vardeclarations are hoisted and initialized with the valueundefined. What this means is that we can use a variable before it is declared but it won't have any value till any one of the assignment statement gets executed.
console.log(test); // undefined
// It didn't throw an error 🙂
var test = "Hello";
let
-
letkeyword is used for creating block scoped variables. - Unlike
var, we can't have two variable declarations usingletwith the same name inside the same scope. It will throw an error.
let test = "Hello";
let test = "Bad";
// SyntaxError: Identifier 'test' has already been declared
- The scope of a variable declared with
letis the curly brackets containing the variable and for the global scope, it is accessible after the declaration throughout the program.
if (true) {
let test = "Hello";
console.log(test); // Hello
}
console.log(test); // ReferenceError: test is not defined
-
letdeclarations are also hoisted but not initialized. That means accessing a variable before its declaration will throw an error.
console.log(test); // ReferenceError: test is not defined
let test = "Hello";
- Same as
var, when usinglet, we can modify or re-assign any type of primitive values or reference values.
const
-
letandconstare the same. The only difference is in the modification and re-assignment of the variable. - All the variables declared using
constand having a primitive value can't be modified or re-assigned. It will throw an error if tried to do so.
const test = "Hello";
test = "Wow"; // TypeError: Assignment to constant variable.
- All the variables declared using
constand having a reference type value, can be modified but can't be re-assigned.
const test = ["Hello"];
test.push("World");
console.log(test); // ["Hello", "World"]
test = ["Wow"]; // TypeError: Assignment to constant variable.
Now let's demystify some of the magical cases.
Case 1
- Let's try to assign a value to a variable before its declaration with
let(orconst) and see what happens.
test = "Bad";
// ReferenceError: Cannot access 'test' before initialization
let test = "Hello";
- As expected, this gives an error. But a lot is going on here and let's try to understand it.
- Here
testis declared usinglet, so it will get hoisted, but it won't get initialized. Since it doesn't get initialized, trying to assign it a value will give an error "Cannot access 'test' before initialization". - Now let's try to do the same thing with
varand see what happens.
console.log(test); // undefined
test = "Wow";
console.log(test); // Wow
let test = "Hello";
console.log(test); // Hello
- Here
vardeclaration is first hoisted and then initialized with theundefinedvalue which is why the first console will printundefined. - Then as the variable is initialized assigning a value
Wowto it works fine and the second console printsWow. - When the Javascript engine comes to the
letdeclaration it simply assigns the valueHelloto it and that is why the third console printsHello.
Case 2
- Let's see an interesting case with hoisting and variable shadowing.
let test = "Hello";
if (true) {
let test = "Wow"; // Will this throw an error???
console.log(test); // Will this execute???
}
console.log(test);
- Let's try to dissect it.
- Here we have declared a variable named
testand initialized it with the valueHello. - Then when it enters the
ifblock, it will create a new scope. As always, Javascript will hoist the declaration of thetestvariable and it won't get initialized as it is declared usinglet. - Then the Javascript engine will assign it the value
Wow. It will work as theletis block scoped and Javascript can have the same named variables in different scopes. - Now when we reach the console Javascript engine will try to find the variable in the current scope and as the current scope has the variable with the name
testit will use it and it won't use the variable from the parent scope. This is called variable shadowing. - As the inner variable's scope is over with
if's curly brackets, the last console will printHello. - Let's look at an example with a small variation.
let test = "Hello";
if (true) {
console.log(test); // 🤔
let test = "Wow";
console.log(test);
}
console.log(test);
- Here when the Javascript engine enters the
ifblock, it will create a new scope. As always Javascript engine will hoist the declaration of thetestvariable and it won't be initialized as it is declared usinglet. - So as we can guess now there is a variable with an uninitialized state in the current scope so Javascript won't use the parent value and throw
ReferenceError: Cannot access 'test' before initialization. - Now let's look at the same example using
var
var test = "Hello";
if (true) {
console.log(test); // 🤔
var test = "Wow";
console.log(test);
}
console.log(test);
- Here when the Javascript engine enters the
ifblock, it will create a new scope. As always Javascript will try to hoist the declaration of thetestvariable but the variables declared usingvarare not block scoped, they are function scoped. - Javascript engine will not hoist it as a same named variable is already there in the current scope. So the first console will use the value from the parent which is
Hello. - When the engine reaches the declaration of the
testvariable inside theifblock it is treated as the declaration of the same named variable as thevaris function scoped and the engine will simply assign the valueWowto thetestvariable and the second console will printWow. - As the parent variable is re-assigned with the new value, the third console will also print
Wow.
Bear with me there is more 😁
Case 3
- Let's look at an interesting case of
varinside theifblock.
if (false) {
var test = "Hello";
}
console.log(test); // Reference error??? 🤔
- Here as we can see that the if block doesn't get executed as the condition, is false, so it should throw a Reference error. Right? Right???
- Well here it won't throw a Reference error and instead, it prints
undefined🙂. - The reason for this is that the Javascript engine still hoists the
testvariable even if this code doesn't get executed and our global scope is now polluted with an extra unnecessary variable. One of the reasons why you should avoid usingvar😅. - In the older code you may see an interesting pattern called IIFE - Immediately Invoked Function Expression through which people avoided the scope pollution.
if (false) { // or true
(function () {
var test = "Hello";
// Some code that uses test
})(); // Note the invocation here
}
console.log(test); // ReferenceError: test is not defined
- Here we have created an anonymous function and immediately called it. Javascript treats it as an expression(thus IIFE).
- As we know that the
varis function scoped and thus it can't be accessed outside the anonymous function.
Case 4
- Let's look at some of the weird cases of the variables declared using
varin the case offorloops. Let's start with a simple example.
for (var i = 0; i < 3; i++) {
// Do something
}
console.log(i); // 3
- As we can see here that the console prints the value
3and that is because the variables declared usingvarare function or global scoped and not block scoped. So hereiis accessible even after theforloop. Again scope pollution 🙂. - Let's look at another famous
forloop problem withvar
var fnArray = [];
for (var i = 0; i < 3; i++) {
fnArray[i] = function () {
console.log(i);
};
}
for (var j = 0; j < 3; j++) {
fnArray[j]();
} // 0, 1 and 2 ??? 🙂
- Here we may think that it should print
0,1and2but it won't and let me tell you why. - Here we have created an array named fnArray and we have pushed some functions in it which is using the variable
ifrom theforloop. - We know that
varis function scoped so its accessibility doesn't have to do anything with theforloop. The function is using the variableibut it will only access its value when it is being executed. - In the last iteration of the first
forloop,i++will be executed with the value2and it will become3which will stop the loop. Now variableiwill be accessible outside theforloop with value3. - Now when the second
forloop gets executed, it will call the anonymous function which will try to console the value of the variableiand as the value ofiis now3it will print3three times. - This problem can be solved easily by using
letin the firstforloop.
var fnArray = [];
for (let i = 0; i < 3; i++) {
fnArray[i] = function () {
console.log(i);
};
}
for (var j = 0; j < 3; j++) {
fnArray[j]();
} // 0, 1 and 2 as expected
- This will work because the
letvariables are block scoped. So each iteration of theforloop will create a scope and it will hold the value ofifor that iteration. - So when the function will try to access the value of
i, it will see the correct value in the scope created by theforloop and print0,1and2as expected.
Summary
So that's it for today folks 😅.
Thanks for bearing with me till the end. Give the post a Heart if you liked the post and give a comment or ping me in case I have missed anything.
You can reach me on:
- Github
- Email : yash.kalaria93@gmail.com

Top comments (1)
Really great article. appreciate your efforts.