Menü schliessen
Created: June 20th 2023
Last updated: January 26th 2024
Categories: Common Web Development,  IT Development,  JavaScript Development
Author: Tim Fürer

JavaScript: Why use let/const over var

Tags:  guide,  Javascript,  web
Donation Section: Background
Monero Badge: QR-Code
Monero Badge: Logo Icon Donate with Monero Badge: Logo Text
82uymVXLkvVbB4c4JpTd1tYm1yj1cKPKR2wqmw3XF8YXKTmY7JrTriP4pVwp2EJYBnCFdXhLq4zfFA6ic7VAWCFX5wfQbCC

The development of JavaScript over the years has seen the introduction of new conventions designed to refine the language and improve its capabilities. A significant milestone was the advent of ES6 in 2015, which introduced "let" and "const", redefining the way variables are declared and managed, and effectively challenging the long-standing dominance of "var".  But why was such a change necessary? And what makes "let" and "const" superior to "var"? What's the difference?


The Quirks of "var" and The Dawn of ES6

Since the inception of JavaScript, "var" was the go-to for variable declaration. However, with the evolution of the language and the growing complexity of JavaScript applications, limitations of "var" began to surface. Among them, the most significant was its function scope, which could cause inadvertent variable leaks and reassignments. Additionally, the hoisting of "var" variables to the top of their scope often led to unexpected results and tricky-to-debug errors.

To address these challenges, the 6th edition of ECMAScript (ES6) was introduced, offering two new keywords for variable declaration – "let" and "const". These new keywords aimed to mitigate the issues presented by "var", bringing in block scoping and eliminating automatic hoisting. Moreover, "const" added the ability to declare variables that could not be reassigned, increasing code predictability.


The Scope Revolution: "let" and "const"

"let" and "const" introduced block scoping to JavaScript, a stark contrast to the function scoping of "var". A block scope restricts a variable's visibility to the block in which it's declared, encapsulating it and preventing it from affecting the rest of the code unintentionally.

This block scoping brought with it a solution to one of the notable quirks of "var" - the optionality of initialisation within an if block. This characteristic of "var" could lead to scenarios where variables remained uninitialised, creating additional potential issues.

Let's illustrate this with an example:

if (false) {
   var result = 'initialised';  // variable is not initialised due to the false condition
}

console.log(result);  // Outputs: undefined

In the above code, result remains uninitialised because the condition in the if statement is false. As "var" is function-scoped, it hoists result to the top of the function, initializing it with undefined and potentially causing confusion when we try to access result later in the code.

The block scoping of "let" and "const", however, provides a solution to this problem:

if (false) {
   let result = 'initialised';  // block-scoped variable isn't initialised
}

console.log(result);  // Throws a ReferenceError

In this case, as result within the if block is block-scoped, it doesn't affect the outer result and the console.log statement throws a ReferenceError since result was not initialised. This behavior makes it clear that the variable hasn't been initialised, making it easier to detect and fix the problem.

Continuing with the previous illustration:

function blockScope() {
   {
      var x = 'var';  // function-scoped
      let y = 'let';  // block-scoped
   }

   console.log(x);  // Outputs: 'var'
   console.log(y);  // Throws a ReferenceError
}

In this function, x is accessible outside the if block because "var" is function-scoped. Conversely, trying to access y outside its block throws a ReferenceError, demonstrating the block scope of "let".


The Constant Guardian: "const"

"let" introduced block scoping and eliminated hoisting. "const", on the other hand, goes a step further by preventing reassignment of the variable. This makes "const" ideal for declaring values that should not be reassignable throughout their scope.

const PI = 3.14;

PI = 3.14159; // Throws a TypeError

In the example above, attempting to reassign PI results in an error, as "const" prevents the reassignment of variables. It's crucial, however, to understand that "const" doesn't make objects or arrays immutable and only prevents reassignment.

const array = [1, 2, 3];

array.push(4); // This is allowed

console.log(array); // Outputs: [1, 2, 3, 4]

As shown, although a "const" array cannot be reassigned, its content can still be altered.


Navigating the Temporal Dead Zone

The introduction of "let" and "const" also brought the concept of the temporal dead zone (TDZ) to JavaScript. The TDZ refers to the time between the start of the block and the line of the variable declaration, during which the variables are considered uninitialised. This is unlike "var", which is hoisted to the top of its scope and initialised with undefined, often causing confusion.

Consider this example:

console.log(a); // undefined
var a = 5;

console.log(b); // Throws a ReferenceError
let b = 5;

In the first part of the code, "var" is hoisted and initialised with undefined. On the other hand, referencing b before its declaration with "let" throws a ReferenceError, making potential issues apparent earlier in the development process.


Conclusion

"let" and "const" present a robust response to the limitations of "var", providing JavaScript developers with more reliable, manageable, and predictable ways to declare variables. With their block scoping, the elimination of hoisting, and in the case of "const", the assurance of non-reassignability, they present a solid foundation for better programming practices. As JavaScript continues to evolve, adopting "let" and "const" over "var" will help produce clearer, more maintainable code, ultimately improving the quality of the software produced.