The Mystery of Variable Declaration in JS

VAR vs LET vs CONST — how to choose right!

Flo Ragossnig ✨
CodeX
6 min readOct 30, 2022

--

Introduction

Although, most of us frontend devs define JavaScipt variables all the time, I have experienced that al lot of, especially junior devs, do not really know or do not really care about the differences of how they declare variables in JS.

I personally find this topic very important since variable declaration (in any language), is such a fundamental part of writing code, that ignoring facts will ultimately lead to performance issues or you find yourself inside debugging hell. Especially with JS, which is, as a subset or pure, still one of the most used languages in frontend applications, performance can quickly become an issue.

Why is this important?

Some of you might ask why even bother since in the end we it doesn’t really matter how we define our variables unless we use things like Typescript. 🤯 Since JS is considered a high level language and thus there is no need for developers to deal with memory allocation for variables in their code, some developers simply ignore the fact that memory still has to be allocated and freed somehow! They simply declare a variable and JS deals with the memory allocation.

However, such memory allocations have to be freed at one point to avoid memory leaks and performance issues. The process of collecting all these dead objects and remove their allocations from the memory, is called garbage collection. Here’s a fantastic article by Irina Shestak about how garbage collection in JS works.

But not only bad memory allocation can become an issue, ignoring best practices can also have implications of code stability. For example, you could unknowingly re-declare a result object somewhere in your code, which results in corrupted data. If your compiler does not warn you, this can quickly lead to a debugging drama which takes hours of your time!

Although, JavaScript is not bad, it still allows you to do a bunch of very weird things — which you as a developer, should be aware of!

Scoping

To understand the subject of declaration differences we first need to establish the definition of scoping. It is not something mysterious, it is basically just the definition space of an object (function, variable, class) inside your code. There is basically two scope definitions: an object is

  • function scoped, if it is accessible on the root level of your code, and
  • block scoped, if the object is only accessible within a block (eg. if statements, nested functions, etc.).

The significance of scoping might not yet be clear but we will focus on this issue in a later example.

Hoisting

Hoisting is the default behaviour of the JS interpreter to move all declarations to the top of their scope to make them accessible throughout their definition space. These declarations include functions, variables and classes although hoisting is often referred to as a feature of var declarations. According to MDN, any of the following behaviours can be considered as hoisting:

  1. Value hoisting → access the value of the variable BEFORE the variable has been declared.
  2. Declaration hoisting → reference the variable prior to declaration but with the value being assigned to undefined .
  3. Lexical hoisting → referencing the variable before declaration or outside its scope leads to a Reference Error.

Usually, function declarations (function , async function ) are subject to type 1 hoisting, var is an example of type 2 and let , const and class (lexical declarations) behave like a type 3 hoisting.

Since this article focuses on variable declarations, let’s finally talk about the different declaration options in JS.

The VAR — declaration

The var declaration is basically a relict from times before ES6 but is still creeping up regularly in modern applications.

In my opinion, var should not be used in any modern applications, apart from maybe some very few exceptions.
❌ — JUST DON’T USE IT!

First of all, var is function scoped and thus can cause a hell lot of problems! Let’s look at an example:

As you can see, rusty is accessible inside AND outside of the if statement since the interpreter moves it to the top of the parent scope! 🤯

Although this might not seem to be a big issue at first, the variable has been allocated and rests within the memory until the entire function has finished. This means that Garbage Collection can never free the varaible’s allocation since it is reachable for as long as the function is executing!

Imagine a running web application with a bunch of memory intensive objects which are declared in a sub scope (eg. the if statement). Since these objects are still reachable from everywhere in the parent scope, the memory for these object can never be released, even if the child scope where the variables were actually used, is not running anymore! 👎

Furthermore, since var is behaving like a type 2 hoisting, the variable gets initialised with undefined and thus even has a value prior to its actual declaration. Here is an example:

We can see thatrusty is accessible even before the variable has been assigned!

Re-declaration of VAR variables

The, in my opinion, most hideous implication with var declarations is the fact that we can redeclare such variables at any time in our function! This not only allocates a new memory every time we re-declare but also allows for tons of assignment errors.

The LET-declaration

The JS team obviously also agreed on the fact that the behaviour of var can be problematic and thus, with the rise of ES6, introduced the new block-scoped declaration options let and const .

If you aren’t sure how to declare your variables, use let ! It is the preferred way of declaring variables in modern JS applications and causes the least amount of issues.

But let us first discuss the difference to a var declaration by looking at an example:

As you can see, rusty is now block-scoped and only lives inside the block where it has been defined! This means that once this block has terminated, the allocation becomes subject to garbage collection since it cannot be reached anymore. This not only can boost performance but also reduces the risk of overriding global values and types significantly.

Moreover, let can be re-assigned but NOT re-declared! Which means that you are still allowed to assign new values to the variable but you can’t allocate an entirely new memory slot for the variable. So, while this is valid

this is not

Finally, unlike var , let is not initialised with undefined — it’s actually not initialised at all since it is behaving like a type 3 hoisting. Therefore, trying to access the variable before it’s actual declaration, results in a reference error:

The CONST-declaration

The last declaration option to discuss is const . As briefly mentioned in the previous section, const is very similar to let . Variable declarations with const are block-scoped and cannot be re-declared. In some cases you might want an even stricter definition, eg. if you know that the value of your variable doesn’t change after your declaration statement. This is exactly when const comes into play. A variable declared with const fixes its value within the scope of the variable:

That’s it! 🚀

I hope this article helps you to be a bit more aware of variable declarations in JS and their implications on your work! Maybe you even start to understand the pedantic advocating by your backend engineer for strict rules in programming languages. 👀

As always, reach out if there are any questions or comments! 🙏

--

--

Flo Ragossnig ✨
CodeX
Writer for

CTO & Lead developer (fullstack) at codeversity.com. In #love with Typescript 🎯, #passion for React ❤️‍🔥, #devoted to Relay 🎉, #affected to GraphQL 🚀