Var, let and const variables. How they differ and which one to use

JavaScript provides three methods for declaring variables. We can use the var, let, and const keywords. This abundance of choices is a source of confusion for a beginner developer.

At least it was for me.

While taking my first steps in the world of JavaScript. I wondered, “Why should a programming language use so many ways to make the same thing?”.

The fact is that this didn’t happen by design but because of the ever-evolving nature of JavaScript.

Each year in JavaScript, new things are added or old things are being depreciated.

From the beginning, and until the rise of ES6, the only way to define a variable was by using the var keyword. The const and let keywords were introduced later, with ES6, as a means to overcome the problems caused by the use of the var keyword.

We are going to address these problems and see how const and let made a difference, and also which to use and when in the following article.

JavaScript Hoisting

Before we move on to the variable declarations, we should talk about the concept of hoisting in JavaScript. Hoisting refers to the process of moving a variable to the top of its function, block, or script source.

Below is an example of how hoisting can accidentally make us declare a global variable:

Because i wasn’t properly declared, it leaks out of the function and becomes accessible globally.
(click on the image to open in a new tab)

Here, the variable i is hoisted at the top of the script source, all the way up to the Window object.

The strange thing is that i in the example is not declared but implied.

In JavaScript, any variable we don’t declare becomes a property of the global (‘Window’ in our environment) object.

For example:

Here, 5 is property of the global object even if we haven’t explicitly declared it as such.
(click on the image to open in a new tab)

So, the previous example is similar to:

Here, we explicitly declared i as property of the global object.
(click on the image to open in a new tab)

JavaScript, under the hood, pushes the variable i up to the top (hoisting), and declares it right there as a property of the Window object.

We see that it is very easy to declare a global variable accidentally. Our intention for i was for it to be a local variable, but by forgetting to properly declare it, we now have a mess.

The var variable

How hoisting affects var

A var variable declared anywhere in the code is hoisted at the top. This provides, as a result, the flexibility for a var variable to be accessed BEFORE it’s declared.

A var variable can be accessed BEFORE it’s declared.
(click on the image to open in a new tab)

Here, the variable sum_var is subject to hoisting, thus accessing it in line 1 logs “undefined” without producing an error.

Once declared, the variable has been moved to the top of the scope, and when the console object logs the variable’s value, it prints “undefined” since every var variable at the time of declaration evaluates to “undefined”.

The problems caused by the use of var have mainly to do with the concept of scope.

Let’s see an example of a for loop.

Here, i had a value of 5, the last time it was called in the loop, and outside the loop has the value of 6.
(click on the image to open in a new tab)

We see that i is mutating outside of the for loop scope. It had a value of 5, the last time it was called in the loop, and outside the loop has the value of 6.

In programming languages like PHP and Java, that wouldn’t be possible because the i variable could only be scoped to the context of the loop and not outside of it.

But in the pre-ES6 JavaScript era, there was only one type of variable scope, and that was function scoped and not block scoped.

For example:

Here, we see that a variable declared inside a function is not accessible outside that function.
(click on the image to open in a new tab)

Variables we declared with the var keyword are function scoped.

Namespace pollution in var

Another characteristic of the var variable is the possibility of duplicate variable declarations. Even in strict mode, var permits duplicate variable declarations without throwing an error.

For example:

i is declared two times without throwing an error.
(click on the image to open in a new tab)

The code, whice should be in distinct namespaces, is now part of a shared namespaces.

Let’s see what happens in the same example if we fail to properly declare the variable i in the loop:

Here, the var i is reassigned in line 5 as i=1.
(click on the image to open in a new tab)

Here, the var i is reassigned in line 5 as i=1. The two variables inside and outside of the for block are considered one since the implied i is hoisted on the global scope.

This issue is less likely to occur in a small application.

But since introducing a variable with the same name does not throw an error, fixing bugs in a large-scale application becomes a chore.

To remedy these issues, caused by the use of the var statement, the ECMA organization introduced the const and let statements in ES6 version of JavaScript.

The const variable

When we use the const keyword to declare a variable within a block, its scope will only be within that specific context.

Declare a variable within a block:

The variable car declared inside a block is not accessible out of that block.
(click on the image to open in a new tab)

Declare a variable within an If statement:

The block scope is applied inside an If statement.
(click on the image to open in a new tab)

Contrary to var variables, const variables are not function scoped:

The variable message is not accessible inside the entirety of the function but only inside the If statement.
(click on the image to open in a new tab)

In the example, the message variable is only accessible inside the if...else block. When we try to log the variable inside the function’s scope, we get an error.

const variables must be initialized upon time of declaration:

With const declaring a variable is not enough. We must also initialize it.
(click on the image to open in a new tab)

In the case of a const variable, the value ‘undefined’ is not automatically assigned upon declaration, thus we have to provide our own value immediately.

This variable works just fine.
(click on the image to open in a new tab)

A const variable is not hoisted at the top, so it can’t be accessed BEFORE its declaration:

We can access the some_var variable before it is declared.
(click on the image to open in a new tab)

Once we declare a const variable, we cannot reassign it to point to a different value.

Reassignment is also impossible with const variables.
(click on the image to open in a new tab)

const and object mutability

Objects in JavaScript are passed by reference. This means when we assign an object to a variable, we’re not pointing to the object itself, but rather to the memory location of the object.

Mutating object properties

Even though we can’t reassign the const variable to a new object, we can still modify the properties within the original object. This is because we’re still referencing the same object in memory.

In the example, we tried to reassign (line 7) the const variable car, and we got an error as a result.
(click on the image to open in a new tab)

But we can mutate properties:

We can modify the properties within the car object.
(click on the image to open in a new tab)

The let variable

let variables, just like var variables, can be defined without being initialized. They are assigned by the engine the value ‘undefined’ when declared.

The lastName variable is assigned the value ‘undefined’ automatically.
(click on the image to open in a new tab)

let variables are mutable.

We see that changing value for the lastName let variable is acceptable.
(click on the image to open in a new tab)

We see in the example that changing a primitive’s value is acceptable.

Other than that, the let variable is identical to the const variable.

Which one to use?

The choice should obviously exclude the var statement, and be made between let and const.

The popular approach, which I adhere to, is to use the const keyword for all the variables that we don’t plan to reassign at some point in the future. For these, we use the let keyword.

We do this because we want to avoid a mutable state, at least when there is no reason for it to exist. Immutable code is safe code.

For example:

Provided that the rectangle is not flexible, there is no need to change its size at some point in the future.
(click on the image to open in a new tab)

Here, we calculate a rectangle’s width. There is no reason for that width to change later.

A score always changing during the duration of the game.
(click on the image to open in a new tab)

But here we need the variable to be mutable. We keep score and will continually change throughout the duration of the game.

Another example of having a reason to declare a mutable variable is in a for loop:

In the code, the i variable is being reassigned in each iteration of the loop.
(click on the image to open in a new tab)

The let i = 1 statement initializes the i variable with the value of 1. Then, during each iteration of the loop, the i++ expression increments the value of i by 1.

This is an example of variable assignment. The let variable allows us to do that. We couldn’t achieve that with a const variable though.

Consts doesn’t allow reassignement in every iteration.
(click on the image to open in a new tab)

The declaration is only evaluated once before the loop body is executed, so we get 1. The reason we’re getting an error is that we’re trying to reassign a value to a constant variable i inside the loop, which is not allowed.

In JavaScript, variables declared with const cannot be reassigned after they are initialized. In our loop, we are trying to increment i with i++, which is a reassignment and not allowed for const variables.

We could use let instead.

NOTE: we can use const with for...of loops

It’s perfectly all right to use const with a for…of loop.
(click on the image to open in a new tab)

With a for…of loop, we can use const instead of let because the variable here is not being reassigned on each iteration of the loop. Instead, it is being declared as new variable in each iteration and assigned the value of the current element in the array.

The const keyword used to declare the variable cat (line 3), creates a new block-scoped variable in each iteration of the loop. This means that the variable cat only exists within the scope of the loop iteration and is not accessible outside of that scope.

Although my blog doesn’t support comments, feel free to reply via email or X.