var, let, and const – Why to avoid var 😷 and how to put the other two to good use? - Javascript

Well, in this blog, we will illustrate the differences between var, let and const. We'll also discuss why we should avoid var and prefer const.

In ES5, we had only one way of declaring variables using var. But with ES6 we now have three to declare a variable namely var, let and const.

1. var

Firstly let us see how var works:

for (var i = 0; i < 10; i++) {
  console.log(i);
}

In most programming languages, the scope of iwill be inside the body of loop only. The value of i can be accessible within the body of for loop.

Let us see what happens if we console the value of variable i after the loop.

for (var i = 0; i < 10; i++) {
  console.log(i);
}
console.log("After loop ", i); //This prints 10. 😳 Why is that?

We can see that we are able to access the variable i even outside the loop, which is very intuitive when coming from another programming language. No one would expect the variable i to survive outside the loop.

Why does that happen🤔? The reason is that there is only one type of variable scope in ES5, which is a function scope.

To illustrate the above statement, let us wrap the above code inside a function and execute the function.

function counter() {
  for (var i = 0; i < 10; i++) {
    console.log(i);
  }
  console.log("After loop ", i); //still prints 10 🤥
}
counter();

Let us move the console.log() to outside of the loop and see what happens.

function counter() {
  for (var i = 0; i < 10; i++) {
    console.log(i);
  }
}
console.log("After loop ", i); //It throws an error this time: Reference error: i is not defined. 🤤
counter();

This time it throws an error Refrence error: i is not defined and it is because the scope of var is within the f unction scope . The variable i will be available only within the function counter and if called from outside the function it will throws and error.

In order to avoid this type of error, Some developers used the concept of Immediately Invoked Function Expression (IIFE).

This is how IIFE looks like.

(function () {
  for (var i = 0; i < 10; i++) {
    console.log(i);
  }
})();
console.log("After loop ", i);

Most of the Javascript developer does not know about IIFE.

IIFE is a function that is declared and called immediately.

People do this to keep the variables that are inside the IIFE to pollute the rest of the code. It creates lots of cohesive block of safe-heaven.

Javascript variables are hoisted

What do Javascript variables are hoisted means? Well, it means that they are hoisted to the top of their function.

What Javascript compiler or interpreter does is it will go through the code, and it will find variable declaration and push them up to the top of the function.

70% of javascript developers do not know the concept of hoisting.

Upon compiling the below code it still works. A the variable i of for loop is hoisted to top of the function.

(function () {
  var i; //It pushes the variable to top of the function.
  for (i = 0; i < 10; i++) {
    console.log(i);
  }
})();

Keep note it doesn't matter how many lines of code are there in between, it will still move the variable declaration to the top of the function.

There is also a horrible aspect of hoisting. Let's say you forgot the variable declaration. as shown in below code

(function () {
  for (i = 0; i < 10; i++) {
    console.log(i);
  }
})();
console.log("After loop ", i); // This prints 10. What the heck just happened??😧

Why does it prints 10? This happened because the javascript interpreter has put the variable declaration out of the function this time.

If there is no variable declaration inside a function the interpreter will keep going up the function chain all upto global/window object. And then if it finds it then it will place the variable in the global/window object.

var i;
(function () {
  for (i = 0; i < 10; i++) {
    console.log(i);
  }
})();
console.log("After loop ", i);
  • This is of course horrible because you can accidently declare a global variables.

  • This is why we are encouraged to always use use strict statement, it prevents us from accidentally declaring a global variable.

"use strict";
(function () {
  for (i = 0; i < 10; i++) {
    console.log(i);
  }
})();
console.log("After loop ", i);

This time the above code will throw an error RefrenceError: i is not defined.

"use strict";
var i = 9999;
(function () {
  for (var i = 0; i < 10; i++) {
    console.log(i); // this prints 1 2 ...10
  }
})();
console.log("After loop ", i); // This prints 9999

Even though we are using the same variable name, it prints 9999 because the inner var i = 0 is scoped to the function only.

This time let's remove var from inside the function and see what happens

"use strict";
var i = 9999;
(function () {
  for (i = 0; i < 10; i++) {
    console.log(i); // this prints 1 2 ...10
  }
})();
console.log("After loop ", i); // This prints 10

It prints 10, this is because i inside function is reassigning the outside variable i. As mentioned above it will check if variable i is defined in function, if it doesn't found it then it will move up to the global/window. As it finds the variable i then it will be re-assigned. That is the reason why it is logging out 10.

Always remember the concept of hoisting. Hoisting is nothing but moving the variable declaration to the top of the function or global/window object.

2. let

Now let us demonstrate the same example using let.

"use strict";
var i = 9999;
for (let i = 0; i < 10; i++) {
  console.log(i);
}
console.log("After for loop", i); // This prints 9999. It works.

We can see from the example code above that let is not behaving like var. And the above code works as expected. The reason why it worked is that let introduces block scoping. The variable let i = 0 of for loop will be available only inside the body of for loop.

The block scoping of let is also valid for if/else, switch etc.

"use strict";
var i = 9999;
for (let i = 0; i < 10; i++) {
  console.log(i);
}
if (true) {
  let i = 1000;
}
console.log("After for and if", i); // This still prints 9999.

From the code snippet we can see that the the scope of variable i is scoped with in for loop and if respectively.

Just like var we can re-assign let.

let i = 10;

i = 57;

console.log(i); // Prints 57

let is block scope while var is function scope.

A lot of people say that let is the new var and I agree with that.

3. const

const is a variable just like let which cannot be re-assigned. To demonstrate this, let me show you an example.

const i = 10;

i = 57;

console.log(i);

The code above throws an error TypeError: Assignment to constant variable.

Let's look at an example below

const x = {
  y: 10,
};
x.y = 15;

console.log(x); // Prints {y: 15}

We can see that we are able to update the property of x. Now let us try to assign a new object to constant variable x.

const x = {
  y: 10,
};
x = { z: 11 };
console.log(x); // This time it throws an error

The code snippet above throws an error because we are re-assigning a constant variable.

var vs let vs const

Whole blog summarized in a table.

Scopes var let const
Stored in global scope true false false
Function Scope true true true
Block Scope false true true
Can be re-assigned true true false
Can be re-declared true false false
Can be hoisted true false false

Conclusion

  • Always use const if you do not want to change the variable.
  • Whenever you are using let first ask yourself if you are really changing the variable or do you really need to change it. If the answer is yes then use let else use const.
  • Always use 'const'. If you REALLY need to change state, use 'let'. 'var' is dead.
  • var should always be avoided because of hoisting.

In this blog we have learnt about

  • Why to avoid var.
  • What hoisting in javascript is.
  • What Immediately Invoked Function Expression (IIFE) is and how to use it.
  • How to decide between using let and const.

💌 If you’d like to receive more tutorials in your inbox, you can sign up for the newsletter here.

Discussions

Up next