
Let vs Const vs Var in JavaScript: Scope, Hoisting, and Best Practices
Variable declaration is one of the first concepts developers learn in JavaScript. Historically, the var keyword was the only option. However, var introduced quirks (such as scope bleeding and unexpected undefined states) that led to difficult-to-trace bugs.
To address these flaws, ECMAScript 2015 (ES6) introduced the let and const keywords.
In this guide, we will analyze the technical differences between var, let, and const, examine block scope, explain variable hoisting and the Temporal Dead Zone (TDZ), and establish best practices for your code.
1. Scope: Function vs. Block
The core difference between these keywords is how their scope boundaries are defined:
varis Function-Scoped: If you declare a variable usingvarinside a function, it is accessible anywhere inside that function. However, if you declare it inside anifblock orforloop, the variable leaks out into the surrounding function or global scope.
if (true) {
var x = 10;
}
console.log(x); // Outputs 10. The variable leaked outside the block!letandconstare Block-Scoped: Variables declared usingletorconstare strictly bound to the nearest block (enclosed in curly braces{}). They cannot be read outside that block.
if (true) {
let y = 20;
}
console.log(y); // ReferenceError: y is not defined2. Hoisting and the Temporal Dead Zone (TDZ)
In JavaScript, variable declarations are lifted (hoisted) to the top of their enclosing scope during compilation. However, how they are initialized differs:
var Hoisting
When var is hoisted, it is initialized with a value of undefined. You can access a var variable before its actual declaration line without triggering an error:
console.log(myVar); // Outputs: undefined
var myVar = 'hello';let and const Hoisting (The TDZ)
let and const declarations are also hoisted, but they are not initialized.
The period between the beginning of the block scope execution and the line where the variable is declared is called the Temporal Dead Zone (TDZ). If you attempt to access a let or const variable inside the TDZ, the JavaScript engine throws an error instantly:
console.log(myLet); // ReferenceError: Cannot access 'myLet' before initialization
let myLet = 'world';This prevents developers from reading uninitialized states, eliminating silent bug variables.
3. Redeclaration and Reassignment
- Redeclaration:
varallows you to declare the same variable name multiple times in the same scope.letandconstthrow a SyntaxError if you attempt duplicate declarations. - Reassignment:
varandletallow you to change their values over time.conststands for "constant" and cannot be reassigned:
const maxLimit = 100;
maxLimit = 200; // TypeError: Assignment to constant variable.const and Object Mutation
A common point of confusion is how const handles arrays and objects.
Declaring a variable as const prevents reassignment (meaning you cannot point the variable name to a different memory address). However, it does not make the underlying data immutable. You can mutate the keys or items inside a constant object or array:
const user = { name: 'Alex' };
// This is permitted (mutation)
user.name = 'Bob';
// This throws an error (reassignment)
user = { name: 'Charlie' }; If you require absolute immutability, you must use Object.freeze(user).
Recommended Style Rules
To maintain code reliability, follow these style rules:
- Use
constby default: Declaring variables asconstsignals to other developers that the variable name will not be repointed, preventing accidental side-effect modifications. - Use
letonly when you expect reassignment: Ideal for loops, counters, mathematical accumulations, or state toggles. - Never use
var:varis legacy syntax. It has no benefits in modern ES6+ environments.
Conclusion
Understanding the scope boundaries and hoisting mechanics of JavaScript variable keywords is essential for writing clean code. By eliminating the function-scoped var keyword and relying on the block-scoped safety of const (for immutable references) and let (for reassignable states), you can write clean, predictable JavaScript.