JavaScript Variable Declarations: var, let, and const
Understanding the differences between var, let, and const for modern JavaScript development
Introduction
JavaScript has three ways to declare variables: var, let, and const. Understanding their differences is crucial for writing modern, bug-free JavaScript code.
Quick Comparison
| Feature | const | let | var |
|---|---|---|---|
| Can Be Reassigned | No | Yes | Yes |
| Can Be Redeclared | No | No | Yes |
| Hoisting | No | No | Yes |
| Scope | Block | Block | Function |
const
const is for values that should never be reassigned.
const PI = 3.14159;
const API_URL = 'https://api.example.com';
// ❌ Error: Assignment to constant variable
PI = 3.14;
Important: const doesn't make objects immutable—it only prevents reassignment.
const user = { name: 'John' };
user.name = 'Jane'; // ✅ Allowed (object mutated)
user = {}; // ❌ Error (reassignment)
Use const by default for all declarations that don't need reassignment.
let
let is for variables that need to be reassigned.
let count = 0;
count = 1; // ✅ Allowed
let user = null;
user = { name: 'John' }; // ✅ Allowed
let has block scope—it's only available within the nearest {}.
if (true) {
let blockVar = 'inside';
console.log(blockVar); // 'inside'
}
console.log(blockVar); // ❌ ReferenceError
var
var is the old way of declaring variables. Avoid using it in modern code.
var name = 'John';
var name = 'Jane'; // ✅ Allowed (redeclaration)
var has function scope, not block scope.
function example() {
if (true) {
var functionVar = 'inside';
}
console.log(functionVar); // 'inside' (leaks out of block!)
}
var is hoisted to the top of its function scope.
console.log(name); // undefined (not ReferenceError!)
var name = 'John';
// Equivalent to:
var name;
console.log(name);
name = 'John';
Modern Best Practices
Default to const
// ✅ Good practice
const name = 'John';
const age = 30;
// Use let only when needed
let count = 0;
count++;
Avoid var
// ❌ Old style
var i = 0;
// ✅ Modern style
let i = 0;
Block Scope Awareness
// ✅ let/const respect block scope
for (let i = 0; i < 5; i++) {
// i is only available in this block
}
console.log(i); // ❌ ReferenceError
// ❌ var leaks
for (var j = 0; j < 5; j++) {
// j leaks out
}
console.log(j); // 5 (accessible!)
Common Pitfalls
const with Objects
const obj = { a: 1 };
obj.a = 2; // ✅ Allowed (mutation)
obj = { a: 3 }; // ❌ Error (reassignment)
// For true immutability, use Object.freeze()
const frozen = Object.freeze({ a: 1 });
frozen.a = 2; // ❌ Error in strict mode
Hoisting Confusion
// var is hoisted
console.log(x); // undefined
var x = 5;
// let/const are hoisted but not initialized
console.log(y); // ❌ ReferenceError (Temporal Dead Zone)
let y = 5;
When to Use Each
Use const
- Configuration values
- Function declarations
- Imported modules
- Any value that shouldn't be reassigned
Use let
- Loop counters
- Accumulators
- State that changes over time
- Temporary variables
Avoid var
- In modern JavaScript (ES6+)
- When block scope is needed
- When you want to prevent redeclaration
Conclusion
Modern JavaScript development follows this hierarchy:
- Default to
const - Use
letwhen reassignment is needed - Avoid
var(legacy code only)
This approach leads to more predictable, maintainable code.
#javascript #es6 #best-practices