Variables, scope and hoisting
var versus let and const, the temporal dead zone, and what hoisting really moves.
var, let and const all declare variables, but they differ in where
the variable is visible and what happens before its line runs.
Interviewers ask this because scope bugs — a loop counter leaking, a
variable read too early — are silent and everywhere.
Real-life situation
A checkout page applies a member discount inside an if block. With var,
the discount variable leaks out and a later line reuses its stale value for
guests. With let and const, each variable stays inside its block and the
bug cannot happen. The runner below shows both behaviors side by side.
Visual
Creation before executionbrowser
Execution context
Memory after creation
empty
Scope
empty
Console
—
Before line 1 runs. JavaScript scans the whole scope first — this scan is the creation phase. Nothing has executed yet.
console.log("Signed in as", userName); // undefined, not an error
console.log("Total:", total()); // works!
console.log("Plan:", plan); // ReferenceError
var userName = "Meera";
function total() { return 2 + 2; }
let plan = "yearly";Try it
function checkout(price, member) {
let total = price;
if (member) {
const discount = price * 0.1;
total = total - discount;
}
return total;
}
console.log(checkout(100, true));
console.log(checkout(100, false));
console.log(oldWay());
function oldWay() {
return status;
var status = "ready";
}
try {
console.log(newWay);
} catch (error) {
console.log("tdz: " + error.name);
}
let newWay = "go";Expected output
90 100 undefined tdz: ReferenceError
Try changing let total to var total (same result here — why?), then move
var status above the return and watch the third line change.
When to use which
Default to const. Use let when reassignment is the point (counters,
accumulators). There is no modern reason to write var — treat it as
read-only legacy you will still meet in old codebases.
What logs first? — guess first, then reveal.
console.log(greet);
var greet = "hi";
console.log(greet);Interview questions
What is the difference between var, let and const?basic
var is function-scoped and hoisted with the value undefined, so it can be read before its declaration line. let and const are block-scoped and live in the temporal dead zone until their declaration runs, so reading them early throws. const additionally forbids reassignment, though it does not freeze objects. Default to const, use let when the value must change.
Interviewers also ask
- What is the temporal dead zone, exactly?
- Can you reassign a const object's properties?
What is hoisting?basic
Hoisting is how declarations are processed before code runs: function declarations and var names are registered during the creation phase, so they exist from the top of their scope. Only the declaration moves, never the assignment — var x = 1 reads as undefined before that line. let and const are also registered but left uninitialized, which is why touching them early throws a ReferenceError instead.
Interviewers also ask
- Are function expressions hoisted too?
What will this log, and why: console.log(x); var x = 5?basic
It logs undefined, not an error. The declaration var x is hoisted to the top of the scope, but the assignment = 5 stays where it is, so at the console.log line x exists with the value undefined. Change var to let and the same code throws, because let variables sit in the temporal dead zone until their declaration line executes.
Interviewers also ask
- How would you fix it?
What is the temporal dead zone?intermediate
The span of code between entering a block and executing a let or const declaration. The name is already registered, so JavaScript knows it exists, but it is deliberately uninitialized — any read or write throws ReferenceError: Cannot access before initialization. It exists to catch the classic var bug of silently reading undefined. The zone ends the moment the declaration line runs.
Interviewers also ask
- Why doesn't var have one?
What is the difference between function scope and block scope?basic
Function scope means a variable is visible everywhere inside the function that declares it — that is how var behaves. Block scope means visibility ends at the closing brace of the nearest block (if, for, or a bare { }) — that is how let and const behave. A let declared inside a loop body, for example, is a fresh variable per iteration, which is why loop closures work with let and break with var.
Interviewers also ask
- Why does let fix the classic loop-with-setTimeout bug?
Are function declarations hoisted? What about arrow functions in const?intermediate
Full function declarations are hoisted with their body, so you can call them before their line in the source. An arrow function stored in const is not callable early: only the const name is registered, and it sits in the temporal dead zone until the assignment runs. Treat declarations as available-everywhere helpers and const arrows as values that must be defined before use.
Interviewers also ask
- When would you prefer one over the other?
What is shadowing, and is it a problem?intermediate
Shadowing is declaring a variable with the same name as one in an outer scope — the inner one wins inside its own scope. It is legal and sometimes handy (a local error hiding an outer one), but it confuses readers and linters flag it because a small edit can silently change which variable a line touches. Rename one of them unless the shadowing is clearly deliberate.
Interviewers also ask
- How does shadowing interact with closures?
Does const make an object immutable?basic
No. const only locks the binding — you cannot point the name at a different object, but you can freely change the object's contents. To freeze contents you need Object.freeze, and even that is shallow: nested objects stay mutable. For a truly independent copy, clone first (structuredClone) and keep the original const.
Interviewers also ask
- What does Object.freeze miss?
Key takeaways
varis function-scoped and reads asundefinedbefore its line.letandconstare block-scoped and throw before their line (temporal dead zone).- Hoisting moves declarations, never assignments.
References
Last verified: 2026-10-12.
Runner helpers
sleep(ms)waits;fakeFetch(url)returns deterministic sample data. Console code runs in a worker with a 5-second limit.