ES6’s finalization in 2015 brought new ways to define JavaScript variables. The let keyword creates a block-scoped variable while const specifies an immutable value. Here’s the lowdown on how these modern variable types differ from the classic var.
Var
Prior to ES6, var was your only option when defining a variable. You can freely change the values of variables created with var. You may also redeclare the variable itself.
Using var creates a variable that’s scoped to the current function. In the event you use it outside a function, the resulting variable will be globally scoped.
The “scope” of a variable describes where it can be used. A function-scoped variable can be accessed by code in the function that defines it. A global variable is accessible throughout your code.
In this example, the difference between globally scoped and function scoped variables are exhibited. myGlobal can be read (and written) by both testA and testB. myFunctionScope is only defined in testA, so testB throws an error when trying to access it. Here’s what this example would output:
The value of myFunctionScope is maintained separately within each function. The value of myGlobal is updated in both functions when testB overwrites it.
Let
The newer let keyword is the modern alternative to var. You can often adopt let in all the places you used to write var. There are important differences to note though.
The most significant let feature is its scoping. Variables are scoped to individual code blocks instead of entire functions. In JavaScript, a block is a section of code that’s wrapped in curly braces. Each let variable is only accessible to code within its block.
In this example, the if statement creates a new code block. Blocks inherit the scope of their parent block so the demo variable remains available. The y variable is scoped to the if statement. Trying to access y outside of the if block results in an undefined variable error.
Like var, variables created with let can have their values changed at any time. They cannot be redeclared though – using let twice with the same name in a single block will cause an error.
An exception is when redeclaring a variable in a nested scope. The rules of block-level scoping mean this is permitted – you end up with two separate variables that happen to have the same identifier.
The above example would emit foobar bar. The foo variable is redeclared in the if block, without impacting the foo variable of the outer scope. You do lose the ability to reference the outer variable from within the inner block.
Const
The const keyword was another ES6 addition. It is block-scoped like let. const is short for “constant” and is used for immutable values that will never change. Trying to update the value of a const variable will always result in an error.
As a consequence, you must always initialise const variables with a value. It’s not permissible to define a const and set its value later on.
Technically, const does not define a constant value. It actually creates a constant reference to a value. The effect of this is that you can still update the properties of objects assigned to a const. Errors only occur if you use the const itself on the left-hand side of an assignment.
Which Variable Type Should I Use?
You should adopt let for most general-purpose variables in your JavaScript code. The block-level scoping and forbidden redeclaration help to catch errors and avoid unintentional overwrites.
Using let stops variable “leakage,” where variables can be accessed in scopes they’re not intended for. A classic example is iterators in loops:
This would result in 10 getting emitted to the console. Using let instead would emit undefined, as the i variable would be inaccessible outside the if. scope. This is usually the desired outcome in this kind of scenario.
Loops also demonstrate the dangers of var reassignment:
At first glance, this code looks like it should emit the numbers 1 to 10. Instead, 10 will be logged ten times. setTimeout() is asynchronous and i in the callback is lexically bound to the scope. As var i is being used in the loop, the i variable gets a new value on each iteration. When the timer callback runs, all ten iterations have already completed and i will always resolve to the final value – 10, in this case.
Using let i instead would declare a new variable called i for each iteration of the loop. Each variable would retain its own value after its iteration completes, resulting in the expected log output.
When Not to Use Let
There are scenarios where you shouldn’t use let. You should use const when you’re creating a variable that you know will never change. Bundlers and static analysis tools will then be able to alert you if you unwittingly try to reassign its value.
It’s usually a good idea to keep variables immutable wherever possible. This helps eradicate bugs caused by unintentional overwrites. Using const also helps you indicate your intent in a codebase by making it explicit that a value won’t change.
ES6 features such as let and const are now universally supported by modern browsers. Some older browsers without full ES6 support, notably Internet Explorer 11, offer them too. You don’t need to worry about whether they’ll be available unless you’re targeting a heavily dated platform. Use let and const to keep your code cleaner and reduce the risk of hidden bugs.