The major difference between var, let, and const is the way they are processed by the JS engine. Unlike other programming languages, the JS engine passes through the code in two phases – The Creation phase & Execution phase.
In the Creation phase, the JS engine goes through the code and reserve memory for the variables being declared. Let’s assume a case where variables are only declared and no value is assigned to these yet.
If the variable is declared with the old-time var keyword, the Initializer assigned a special value “undefined” as its value. On the contrary, if the variable is declared with the let or const keyword, the initializer assigns a special mode called the Temporal Dead Zone for the uninitialized variable.
In the Execution phase, the JS engine replaces “undefined” value of uninitialized var variables with the value being assigned. This is known as hoisting.
For example, for the following code, the JS engine replaces “undefined” with 4 as xx’s value.
Note: You might read in many online tutorials that variables physically move to the top of the code. MDN contradict with this and explains:
Conceptually, for example, a strict definition of hoisting suggests that variable and function declarations are physically moved to the top of your code, but this is not in fact what happens. Instead, the variable and function declarations are put into memory during the compile phase, but stay exactly where you typed them in your code. Reference: https://developer.mozilla.org/en-US/docs/Glossary/Hoisting
If you try to access an uninitialized var keyword, the JS engine displays “undefined” instead, whereas, in case of let or const uninitialized variables, a “reference” error is being generated.
For example, the following code displays an uncaught reference error.
Const variables work similarly as let variables. The only difference being a const that it needs to be initialized at the point of declaration, which can’t be changed later.
As soon as you assign a value to a let variable, the temporal dead zone ends. The main purpose of TDZ is to catch errors. It ensures that variables always have some value at the runtime.