Scope: Where Names Live

You know that variables bind names to values and that functions have their own internal workings. This lesson answers the question those two ideas collide on: when two pieces of code both say total, are they talking about the same thing? Scope is the set of rules that answers it, and it is the last conceptual piece Level 1 needs.

Think of names like the word "kitchen" in a block of apartments. Inside apartment 4B, "the kitchen" means 4B's kitchen. Inside 7A it means a different room entirely, and neither tenant can grab dishes from the other's. But "the lobby" means the same shared space to everyone in the building. Local variables are apartment kitchens; globals are the lobby.

Local versus global

A variable created inside a function is local: it exists only inside that function, is born fresh on every call, and evaporates when the call returns. Two functions can each have a local named total without any relationship or conflict. A global, by contrast, lives at the top level, is visible everywhere, and there is exactly one of it: every reader sees the same value and every writer overwrites it for everyone.

That single shared copy is why experienced programmers treat globals with suspicion. Any function anywhere might have changed one between two reads, so understanding a global-heavy program means understanding every function at once. Locals keep reasoning small: what happens in the call stays in the call.

Shadowing: the nearest name wins

When an inner region declares a name that already exists outside, the inner one shadows the outer for as long as it lives. Inside that region, the name means the local; outside, the outer one is untouched all along. Shadowing is usually harmless, occasionally the source of a maddening bug where you update the wrong total, and always resolved by one rule: the nearest enclosing declaration wins.

Watch names collide

The teaching language has exactly one scope, everything is global, which makes it the perfect demonstration of the disease scope cures. Step through the program below: two unrelated calculations both use a scratch variable named temp, and the second silently destroys evidence the first left behind.

In a language with proper scoping, each calculation would keep its scratch names local and the collision would be impossible. That is what scope buys: the freedom to pick natural names without checking what every other line of the program called things.

Scope in eight languages, plus a first closure

The switcher below shows an inner function updating a variable from the code around it. That is the doorway to closures: a function that captures a variable from its birthplace keeps access to it even as calls come and go. Watch how each language marks the difference between the captured outer variable and a throwaway local.

The closure deserves a beat of respect: bump captures count, and the captured variable outlives any single call, holding state between them. Notice Python needs an explicit global (or nonlocal) declaration to write an outer name, and PHP demands use (&$count), while Lua, JavaScript, Go, and Ruby capture silently. Closures are the standard way to hold private state without classes, and they matter to this course for a concrete reason: a captured variable appears in no global table and no object, so protected code that hides state in closures gives readers one less place to look. Level 7's virtual machines lean on that.

And here is the bridge to everything ahead. Scope is exactly the map a renaming tool must build before it changes anything: renaming a local is safe precisely because scope guarantees every use is inside a known region, while renaming across scopes risks capturing a name into the wrong territory, turning two variables into one. The same map is what a reverse engineer rebuilds when untangling minified code, deciding which of five hundred variables named a are actually the same one. Scope analysis is the shared first step of both jobs, and Level 4 begins there.

Scope gives every name a territory and a lifetime: locals are born and die with their call, globals are one shared copy, and the nearest declaration wins. This map is what makes safe renaming possible, what closures quietly bend by letting captured variables outlive their calls, and what both protectors and attackers must reconstruct before touching anyone's code.

Frequently asked questions

What is the difference between scope and lifetime?

Scope is where a name is visible in the text; lifetime is when the value exists during execution. They usually travel together, a local is visible in its function and alive during the call, but closures split them: a captured variable may be invisible from outside yet still alive, held by the function that captured it.

Are globals always bad?

No, they are a tool with a real cost. A handful of well-named constants and configuration values at the top level is normal. The trouble grows with mutable globals touched from many places, because every reader must then consider the whole program at once. Scope discipline is about keeping the area you must think about small.

Keep learning

  • Functions: Reusable Blocks of Work
  • Imports and Modules
  • Identifier Renaming: The First Layer, Never the Last
  • Where Values Live: Memory, Stack, and Heap
  • Lua Obfuscator
  • JavaScript Obfuscator
  • Python Obfuscator

All lessons