const by default, let when you must reassign, var never — and know why: scope, hoisting and the temporal dead zone.
The three declarations
var | let | const | |
|---|---|---|---|
| Scope | Function | Block { } | Block { } |
| Hoisted? | Yes, initialised to undefined | Yes, but uninitialised (TDZ) | Yes, but uninitialised (TDZ) |
| Re-declare in same scope | Allowed | SyntaxError | SyntaxError |
| Reassign | Yes | Yes | No — TypeError |
Creates globalThis property (scripts) | Yes | No | No |
Block scope vs function scope
function demo() {
if (true) {
var a = "var leaks out of the block";
let b = "let stays inside";
}
console.log(a);
console.log(typeof b);
}
demo();var leaks out of the block
undefinedconst means constant binding, not immutable value
const user = { name: "Asha" };
user.name = "Ravi"; // fine: mutating the object
user.age = 30; // fine
console.log(user);
try {
user = {}; // not fine: rebinding the name
} catch (e) {
console.log(e.name + ": " + e.message);
}{ name: 'Ravi', age: 30 }
TypeError: Assignment to constant variable.Hoisting and the Temporal Dead Zone
console.log(x); // var is hoisted and set to undefined
var x = 5;
try {
console.log(y); // y exists but is in the TDZ
} catch (e) {
console.log(e.name + ": " + e.message);
}
let y = 10;undefined
ReferenceError: Cannot access 'y' before initializationThe TDZ is temporal (about time, not position): a function that reads the variable is fine as long as it is called after the declaration runs.
function read() { return value; } // refers to value declared below
let value = 42;
console.log(read()); // called after initialisation → OK42The classic loop bug
const withVar = [];
for (var i = 0; i < 3; i++) withVar.push(() => i);
console.log(withVar.map((f) => f()));
const withLet = [];
for (let j = 0; j < 3; j++) withLet.push(() => j);
console.log(withLet.map((f) => f()));[ 3, 3, 3 ]
[ 0, 1, 2 ]var i is one variable shared by every closure (it is 3 when they run). let j creates a fresh binding per iteration, so each closure captures its own value.
Naming rules
Letters, digits,
_and$; cannot start with a digit; case-sensitive (name≠Name).Reserved words (
class,return,new…) cannot be names.Conventions:
camelCasefor variables/functions,PascalCasefor classes,UPPER_SNAKE_CASEfor true constants.
What is the difference between var, let and const?
Are let and const hoisted?