Written by Rahul · Frontend Engineer at Google · Updated 2025
Let me save you 10 minutes: use const by default, let when you need to reassign, never use var. But understanding why is what makes you a strong engineer and helps you debug legacy code. Let's dig in.
The Key Differences at a Glance
| Feature | var | let | const |
|---|---|---|---|
| Scope | Function | Block | Block |
| Hoisting | Yes (initialized to undefined) | Yes (but TDZ) | Yes (but TDZ) |
| Re-declaration | ✅ Allowed | ❌ Error | ❌ Error |
| Re-assignment | ✅ Allowed | ✅ Allowed | ❌ Error |
| Global object property | ✅ Yes | ❌ No | ❌ No |
1. Scoping — The Most Important Difference
// var is FUNCTION scoped
function example() {
if (true) {
var x = 10;
}
console.log(x); // 10 — var "leaks" out of the if block
}
// let and const are BLOCK scoped
function example2() {
if (true) {
let y = 10;
const z = 20;
}
console.log(y); // ReferenceError!
console.log(z); // ReferenceError!
}This is why var causes bugs. It ignores blocks (if, for, while) and only respects function boundaries.
2. Hoisting and the Temporal Dead Zone (TDZ)
// var hoists AND initializes to undefined
console.log(a); // undefined (not an error!)
var a = 5;
// let hoists but does NOT initialize — TDZ
console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 5;
// const — same TDZ behavior
console.log(c); // ReferenceError
const c = 5;The Temporal Dead Zone is the period between entering the scope and the declaration line. let and const exist in this zone but can't be accessed — giving you a clear error instead of a silent undefined.
3. const Doesn't Mean Immutable
This trips up so many people:
const user = { name: "Rahul", age: 28 };
user.age = 29; // ✅ This works! You're mutating the object, not reassigning
user = { name: "Someone" }; // ❌ TypeError: Assignment to constant variable
const numbers = [1, 2, 3];
numbers.push(4); // ✅ Works — array is mutated, not reassigned
numbers = [5, 6]; // ❌ TypeErrorconst prevents reassignment of the variable binding, not mutation of the value. If you need true immutability, use Object.freeze().
Real Production Issues
Issue 1: The Classic Loop Bug
Issue 2: Accidental Global Variables
Issue 3: Re-declaration Bugs
Best Practices for 2025
- Default to
const— if you don't need to reassign, useconst. It communicates intent. - Use
letonly when reassignment is needed — loop counters, accumulators, conditionally assigned values - Never use
var— there's zero reason in modern JavaScript. ESLint rule:no-var - Always use strict mode — catches accidental globals
- When working with legacy code, refactor
vartoconst/letcarefully — check for hoisting dependencies
What Google's Style Guide Says
Google's internal JavaScript style guide (and the public one) says: "Declare all local variables with either const or let. Use const by default, unless a variable needs to be reassigned. The var keyword must not be used."
Interview Tip
Don't just list the differences. Show the loop bug, explain TDZ, and mention that const doesn't mean immutable. Interviewers love when you can explain why each difference matters in practice.