Closures & Scope
Scope — Where Variables Live
Every variable in JavaScript has a scope — the region of your code where that variable is accessible. Think of scope like rooms in a house. If you're in the kitchen, you can see everything in the kitchen. You can also see into the living room through the doorway (outer scope). But you can't see into the closed bedroom (another function's scope).
JavaScript has three types of scope:
- Global scope — variables declared outside any function or block. Visible everywhere. Like the hallway — everyone can see it.
- Function scope — variables declared inside a function with
var,let, orconst. Only visible inside that function. - Block scope — variables declared with
letorconstinside curly braces{}. Only visible inside that block.vardoes NOT have block scope — this is one reason to avoid it.
Scope in Action
Hoisting — Variables That Time-Travel
JavaScript does something weird before running your code: it hoists (lifts) declarations to the top of their scope (related to how the call stack manages execution contexts). But here's the catch — only the declaration is hoisted, not the value.
vardeclarations are hoisted and initialized toundefined. You can use them before the line where they're declared (but you'll getundefined).letandconstdeclarations are hoisted too, but they're placed in a "temporal dead zone" (TDZ) — accessing them before declaration throws aReferenceError.- Function declarations are fully hoisted — you can call them before they appear in your code!
- Function expressions & arrow functions follow the rules of their variable (
var/let/const).
Hoisting Surprises
Closures — The Superpower
A closure is when a function "remembers" the variables from the scope where it was created, even after that scope has finished executing. It's like taking a photo at a party — the party ends, but the photo captures everyone who was there.
Closures happen naturally in JavaScript. Whenever a function is defined inside another function, the inner function has access to the outer function's variables — forever. This is how JavaScript implements data privacy, callbacks, and many common patterns.
Closures in Action
Closures in Loops & IIFE
One of the most famous closure gotchas involves loops. With var (which is function-scoped, not block-scoped), all iterations of a loop share the same variable. By the time a callback runs, the loop is done and the variable holds its final value. Using let (block-scoped) fixes this because each iteration gets its own copy.
An IIFE (Immediately Invoked Function Expression) is a function that runs the moment it's defined. Before let and const existed, IIFEs were used to create private scope. You'll still see them in older code and in some build patterns.