JavaScript Variables: The Complete Guide to var, let, and const
JavaScript Variables: The Complete Guide to var, let, and const
Quick Summary: JavaScript offers three ways to declare variables: var (the old way), let and const (modern ES6+). Understanding their differences is crucial for writing bug-free, maintainable code.
📊 Quick Comparison Table
| Feature | var | let | const |
|---|---|---|---|
| Scope | Function | Block | Block |
| Re-declare | ✅ Allowed | ❌ Not allowed | ❌ Not allowed |
| Re-assign | ✅ Allowed | ✅ Allowed | ❌ Not allowed |
| Hoisting | Yes (undefined) | Yes (TDZ) | Yes (TDZ) |
| When to Use | Avoid | Mutable values | Default choice |
1. var – The Legacy Way
Function-scoped, hoisted, and full of quirks
🔍 Key Characteristics:
- Function-scoped (or global if declared outside functions)
- Hoisted and initialized with
undefined - Can be re-declared in the same scope
- Becomes a property of the global object (
windowin browsers)
💻 Code Examples:
// Function scope example
function exampleVar() {
if (true) {
var x = 10; // Function-scoped, not block-scoped
}
console.log(x); // 10 - accessible outside the if block
}
// Hoisting example
console.log(y); // undefined (not an error!)
var y = 5;
// Re-declaration allowed
var z = 1;
var z = 2; // No error2. let – The Modern Standard
Block-scoped, predictable, and recommended
🔍 Key Characteristics:
- Block-scoped (confined to
{}) - Hoisted but in Temporal Dead Zone (TDZ)
- Cannot be re-declared in the same scope
- Can be re-assigned (mutable)
💻 Code Examples:
// Block scope example
function exampleLet() {
if (true) {
let x = 10; // Block-scoped
console.log(x); // 10
}
// console.log(x); // ReferenceError: x is not defined
}
// Temporal Dead Zone (TDZ)
// console.log(a); // ReferenceError: Cannot access before initialization
let a = 5;
// No re-declaration
let b = 1;
// let b = 2; // SyntaxError: Identifier 'b' already declared
// Loop example (solves closure problem)
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // Logs: 0, 1, 2
}3. const – The Constant Champion
Block-scoped, immutable reference, best practice
🔍 Key Characteristics:
- Block-scoped like
let - Cannot be re-assigned after declaration
- Must be initialized during declaration
- Object/array contents can be modified (mutation allowed)
💻 Code Examples:
// Basic const usage
const PI = 3.14159;
// PI = 3.14; // TypeError: Assignment to constant variable
// Objects can be modified
const user = { name: "John", age: 30 };
user.age = 31; // ✅ Allowed (mutation)
user.city = "NYC"; // ✅ Allowed
// user = {}; // ❌ Not allowed (reassignment)
// Arrays can be modified
const colors = ["red", "green"];
colors.push("blue"); // ✅ Allowed
colors[0] = "yellow"; // ✅ Allowed
// Must be initialized
// const VALUE; // SyntaxError: Missing initializer
const VALUE = 100;⏳ Understanding Temporal Dead Zone (TDZ)
The TDZ is the period between entering a scope and the actual declaration where variables exist but can’t be accessed.
// TDZ starts at beginning of scope
console.log(value); // ❌ ReferenceError: TDZ!
let value = "Hello"; // TDZ ends here
console.log(value); // ✅ "Hello"Why TDZ Matters:
- Catches bugs early (use before declaration)
- Makes code more predictable
- Applies to both
letandconst - Doesn’t apply to
var(initialized withundefined)
🎯 Best Practices & Recommendations
Use const by Default
Start with const for all variable declarations. Only use let when you know the value needs to change.
// ✅ Good
const API_URL = "https://api.example.com";
const config = { timeout: 5000 };
const users = ["Alice", "Bob"];Use let for Mutable Values
When a variable’s value needs to change (counters, flags, accumulators), use let.
// When reassignment is needed
let counter = 0;
counter = 1; // ✅
let isLoading = false;
isLoading = true; // ✅Avoid var in New Code
var has quirks that lead to bugs. Use let and const in all new JavaScript code.
// ❌ Avoid
var oldVariable = "outdated";
// ✅ Use instead
const newVariable = "modern";
let mutableVariable = "changeable";🌐 Real-World Example: Loop Behavior
This classic example shows why let is superior to var in loops:
❌ Problem with var
for (var i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i); // Outputs: 3, 3, 3
}, 100);
}All callbacks reference the same i variable.
✅ Solution with let
for (let i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i); // Outputs: 0, 1, 2
}, 100);
}Each iteration gets its own i binding.
💼 Common Interview Questions
Q1: Can you modify a const object?
Yes! You can modify the contents of a const object or array, but you cannot reassign the variable itself.
const obj = { a: 1 };
obj.a = 2; // ✅ Allowed
obj.b = 3; // ✅ Allowed
// obj = {}; // ❌ Not allowedQ2: What’s the output of this code?
console.log(x);
let x = 5;ReferenceError: Cannot access ‘x’ before initialization due to Temporal Dead Zone.
Q3: When should you use let vs const?
Default to const for all declarations. Only use let when you specifically need reassignment capability.
📝 Summary & Final Thoughts
constis your default choice – Use it for 90% of variable declarationsletis for mutable values – When you need to reassign a variable- Avoid
varin new code – Its quirks lead to bugs and confusion - Understand block vs function scope – Critical for predictable code
- Remember TDZ –
letandconstare hoisted but not initialized
💡 Pro Tip:
Configure your linter (ESLint) to enforce using const by default and flag any usage of var as an error.