Scope & var/let/const (the TDZ)
Scope answers one question: where can I see this variable? Getting it right is the difference between a clean program and one full of names that leak, clash, or mysteriously hold the wrong value.
Learn Scope & var/let/const (the TDZ) in our free JavaScript course — a beginner-friendly interactive lesson with runnable examples, a practice exercise and…
Part of the free JavaScript course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
We'll compare var , let , and const head to head, walk the scope chain, revisit the Temporal Dead Zone, and finally explain that famous loop bug.
📚 Prerequisites: Read Hoisting first — scope and hoisting are two sides of the same coin. Comfort with functions helps too.
💡 Running Code Locally: While this online editor runs real JavaScript, some advanced examples may have limitations. For the best experience:
🏢 Real-World Analogy: Scope is like rooms in an office building:
A variable's scope is the region of code where its name is valid. There are three levels: global (the whole program), function (inside one function), and block (inside any {' '} — an if , a loop, a bare block). let and const are block-scoped; var only respects functions.
Here's the headline difference. Declare a var inside an if or loop and it escapes the block — it's visible in the whole function. A let in the same place stays politely contained. This leak is a classic source of subtle bugs.
When you reference a name, JavaScript searches the current scope, then the one enclosing it, and so on outward until it hits global. The first match wins — a phenomenon called shadowing when an inner name hides an outer one of the same name.
Change the declaration so secret stays inside the block and the outer log throws.
This is the interview classic. With var , the loop shares a single i , so every deferred callback reads its final value. With let , each iteration gets a fresh binding, so callbacks capture the value they had at the time. Same loop, one keyword apart.
A common surprise: you can mutate a const object. const only forbids pointing the name at something new — it doesn't freeze the contents. To lock the contents too, reach for Object.freeze() .
Swap one keyword so the callbacks capture 0, 1, 2 instead of all threes.
These lines are scrambled. Reorder them so the inner function can read outer and it logs hi .
Why: inner reads message via the scope chain at call time, so message (a const ) must be initialised before inner() runs.
Predict the output before revealing the answer.
1 — var is function-scoped, so it leaks out of the if block.
2 — const blocks reassigning the binding, but the object's contents can still change.
in out — the inner x shadows the outer one inside f ; outside, the global x is unchanged.
Rule of thumb: default to const , use let when you must reassign, avoid var .
Up next: Map & Set — purpose-built collections beyond plain objects and arrays. 🗺️
Practice quiz
Which keyword is function-scoped rather than block-scoped?
- let
- const
- var
- block
Answer: var. var is function-scoped, so it ignores inner { } blocks; let and const are block-scoped.
What does this log? function demo() { if (true) { var leaky = 'I escaped!'; } console.log(leaky); } demo();
- I escaped!
- ReferenceError
- undefined
- ''
Answer: I escaped!. var leaks out of the if block to the whole function, so leaky is still 'I escaped!' after the block.