var, let, and const - The Three Musketeers

var, let, and const - The Three Musketeers

Understanding the differences between var, let & const

Introduction

In JavaScript, you might have seen the variables being declared using the keywords var, let and const. This can be confusing to understand when to use each or how one is different compared to the other.

Although the three keywords have similarities in syntax, they differ in their usage and scope. Let's break this down to understand each keyword better.

Prerequisites

We can understand the differences between these keywords if we are aware of the following:

  1. Scope
  2. Hoisting
  3. Temporal Dead Zone

Let's quickly look into each of them.

Scope

JavaScript has 3 types of scope:

  • Block scope - Variables declared inside a { } block cannot be accessed from outside the block. This means if you declare a variable within an if-else block or a for loop, you will not be able to access the variable outside the code block.
  • Local or Function scope - Variables declared within a JavaScript function, become local to the function. They are not accessible outside the function block.
  • Global scope - A variable declared outside a function, becomes global and is accessible throughout the script.

Hoisting

Hoisting is a JavaScript mechanism where variables and function declarations are "hoisted" to the top of the current scope (to the top of the current script or the current function).

Check the below code. When we declare the functions and invoke them, we get the expected output.

function sayHi() {
  console.log("Hi");
}

sayHi();

// Hi

But what if we try invoking them before defining them?

sayHi();

function sayHi() {
  console.log("Hi");
}

// Hi

As you can see in the above example, the function sayHi is defined after it is used, yet no errors are thrown. This is because JavaScript is taking that function and essentially moving it to the top of the file, in a way.

What actually happens behind the scenes is that when compiling the code, the browser puts the function into memory before running the code. We will see more examples of this later.

TDZ - Temporal Dead Zone

A temporal dead zone (TDZ) is the area of a block where a variable is inaccessible until the moment it is completely initialized with a value.

If this seems confusing, hold on tight! We will get there in a bit!

Alright, let's dive deeper.

Understanding var

var was the first keyword that was made available in JavaScript to declare variables. But unlike in most other programming languages, var works differently based on how the variable that is created is scoped.

Scope of var

var is globally-scoped or function-scoped, meaning, they can be accessed throughout the code if declared globally, or only within the function block when declared inside a function.

var - globally-scoped:

var message = "Hello!!";

function sayHi() {
  console.log(message); //message is accessible inside the function
}

sayHi(); //Hello!!

var - function scoped

function sayHi() {
  var a = "5";
  console.log(a); //5
}

//throws an error as "a" is not accessible outside the function block
console.log(a); //a is not defined
sayHi();

Hoisting of var

var variables are hoisted to the top of their scope and initialized with a value of undefined.

Look at the code below. We are trying to log the variable message before it is declared. The output is undefined.

console.log(message);
var message = "Hello!!";
//undefined

This can be interpreted as:

var message;
console.log(message); //undefined
message = "Hello!!";

var can be redeclared.

Understanding let & const

let and const was introduced in ES6 as a way of declaring variables.

Scope of let & const

let and const are block-scoped. Variables declared inside a { } block cannot be accessed from outside the block.

{
  let a = 10;
  const b = 20;
  console.log(a); //10
  console.log(b); //20
 }

console.log(a); //a is not defined
console.log(b); //b is not defined

Hoisting of let & const

Variables declared with let and const are also hoisted but, unlike var, are not initialized with a default value. A ReferenceError will be thrown if a variable declared with let or const is read before it is initialized.

  console.log(a);
  let a = 4; 
  //Uncaught ReferenceError: Cannot access 'a' before initialization

On putting a debugger on line 1 of the above code, we see the below memory allocation to the variable a. But this is in a separate memory space and not the Global object, one which can be accessed only when a value is provided to the variable.

image.png

Here's a different scenario:

 let a = 4;
 console.log(a); //4

The variable is hoisted into the other memory space before the execution of the code. On initialisation, the variable is reassigned with the initialised value - in this case, 4 which is then logged as the output.

image.png

image.png

Let's now see what TDZ is:

A temporal dead zone is the area of a block (Script) where a variable (a) is inaccessible until the moment it is completely initialized with a value (a=4).

Did you figure it out yet?

A let or const variable is said to be in a "temporal dead zone" (TDZ) from the start of the block until code execution reaches the line where the variable is declared.

While inside the TDZ, the variable has not been initialized with a value, and any attempt to access it will result in a ReferenceError. The variable is initialized with a value when execution reaches the line of code where it was declared.

Re-Declaring JavaScript Variables

  • A variable defined using var can be redeclared or reassigned with a new value.
var msg = "I can be redeclared!";
console.log(msg); //I can be redeclared!

var msg = "Redeclared value";
console.log(msg); //Redeclared value

msg = "Redefined value of the existing variable";
console.log(msg); //Redefined value of the existing variable
  • A variable defined with let cannot be redeclared but can be reassigned with a new value.
let msg = "I cannot be redeclared";
console.log(msg);
//I cannot be redeclared

msg = "However, I can be redefined!";
console.log(msg);
//However, I can be redefined!

let msg = "redeclaration throws an error here";
console.log(msg);
// SyntaxError: /src/index.js: Identifier 'msg' has already been declared
  • A variable defined with const must be assigned a value when they are declared and cannot be redeclared or reassigned.
const msg = "I cannot be redeclared or redefined";
console.log(msg)
//I cannot be redeclared or redefined

msg = "Reassigning throws an error";
console.log(msg)
// Uncaught TypeError: Assignment to constant variable.

const msg = "Redeclaration throws an error"
console.log(msg)
// Uncaught SyntaxError: Identifier 'msg' has already been declared

Conclusion

Now that you know the differences between var, let and const, when should you use them? It is recommended that you always use const when you don't have to reassign a value to the variable and use let if there is a need to reassign a variable. It is best to avoid using var as its behaviour varies as per the scope, which can lead to unnecessary problems with your code.

That's all, folks! Thank you for sticking to the end, you're a real one! Do let me know your thoughts on this blog post and share it with your folks if you found it helpful!