3.5 — Scope
Here's a question that has quietly followed you since Phase 1: when a line of code says ship, how does the engine know which ship — and why can some lines see a variable while others act like it doesn't exist? The answer is scope: every variable lives inside a bubble, and bubbles nest inside each other like soap bubbles on paper.
The rule of the whole lesson fits in one line: inner bubbles can see outward; outer bubbles can never see in. Master that one-way glass and you'll never be surprised by "is not defined" again — and you'll be ready for closures, the most magical thing in JavaScript, two lessons from now.
let planet = "Earth";
function mission() {
let ship = "Falcon";
if (ship === "Falcon") {
let fuel = 100;
console.log(fuel + " left on " + ship);
}
console.log(ship + " from " + planet);
console.log(fuel);
}
mission();Three bubbles, drawn straight from the braces: the global bubble (the whole file), mission()’s bubble, and the if block’s bubble. Every { } pair blows a new bubble.
The deeper story, with the real names for things — this part is what turns “I saw it” into “I can explain it.”
Real names: each bubble is a scope — global scope, function scope, block scope. What we drew as a ray is the engine walking the scope chain. Current scope first, then outward, one level at a time, stopping at the first match. Exhaust the chain without a match → ReferenceError.
This is called lexical scoping: the chain is fixed by where the code is written (where the braces are), not by how it's called.
Remember leaky var from lesson 1.4? Now you can say precisely why it leaks: var ignores block bubbles and attaches to the whole function bubble. For twenty years that was all JavaScript had — real block scope only arrived with let/const in 2015.
Fun fact: the spec has a deliciously spooky name for one corner of scope. The stretch of code above a let line — where using the name throws an error instead of answering — is officially called the Temporal Dead Zone. Yes, that's the real term standards engineers use, and yes, it sounds like a sci-fi movie.
⌨️ everything at once
A store prints price tags: item, price, and the store’s name on every tag. The function reads a constant from the OUTER bubble — scope in action.
requirements:
- The store’s name —
"Code Corner"— lives in a variable namedshopName, declared OUTSIDE the function. It never changes; store it accordingly. - A function named
priceTagtakes two inputs:itemfirst, thenprice— and its lookup ray reaches outward to findshopName. - Each call prints one tag matching the expected output below exactly — built from the inputs and
shopName, never typed by hand. (Copy the long dash — from the expected output; it’s not the minus key.) - Two tags to print: gear at 5, spring at 2.
when you press RUN, the console must show exactly:
✏️ Quick check 1
The lookup ray travels in ONE direction only. Type it: inward or outward.
✏️ Quick check 2
Code in the global bubble tries to read a let variable declared INSIDE a function. Type the NAME of the error it gets.
✏️ Quick check 3
Two variables, same name, different bubbles. Type exactly what calling f() prints:
let x = "out";
function f() {
let x = "in";
console.log(x);
}
f();🗣️ Now teach it back
Explain scope to a friend using the bubbles-and-ray picture: where do variables live, which direction can lookups travel, and what happens when two bubbles use the same name?
Write it as if your friend is sitting next to you. Saved to your journal — future-you will use these notes to teach others.