JavaScript Scope
You can declare variables, write functions, and return values. One more rule decides whether a name is even reachable: scope. Scope answers “from here, which names am I allowed to use?”
What scope means
Scope is the region of your program where a name exists. Outside that region, the same spelling may be invisible — or it may refer to a different binding. Think of rooms: a label taped to the kitchen wall is easy to see in the kitchen; it is not automatically on the street.
Three everyday kinds
📊 Global, function, and block scope
| Kind | Where the name lives | Typical tools | Visible where? |
|---|---|---|---|
| global | outside every function | let / const / var at top level |
across the script (same page) |
| function (local) | inside a function body | parameters and locals | only inside that function |
| block | inside { } such as if or a loop |
let and const |
only inside that block |
These three cover most beginner code. Function locals stay private. Block bindings with let and const stay inside their braces. Globals are shared — useful for a shared counter, risky if every name becomes global.
let city = "Tehran";
function showCity() {
console.log(city);
}
showCity();
console.log(city);
Global names sit outside functions
city is declared at the top level, so both the function and the later console.log can read it. Change the string once; every reader sees the new value. Prefer globals only when several parts truly share one value.
function greet() {
let message = "Hello";
console.log(message);
}
greet();
// console.log(message); // Error: message is not defined
Locals stay inside the function
message exists only while greet runs. Outside the function, that name is not defined. Parameters work the same way: they are local to the function that lists them.
if (true) {
let score = 10;
console.log(score);
}
// console.log(score); // Error: score is not defined
let and const respect blocks
Inside { }, a let or const binding is visible only in that block. After the closing brace, score is gone. Older var is function-scoped instead — it can “leak” past a block. Prefer let and const so block edges mean what they look like.
let title = "Shop";
function show() {
let label = "open";
console.log(title + " is " + label);
}
show();
Nested code looks outward
show can read title from the outer scope and label from its own. Lookup walks outward until it finds a match. It does not walk inward: outer code cannot see a name born only inside the function.
let status = "ok";
function check() {
let status = "busy";
console.log(status);
}
check();
console.log(status);
Shadowing: the inner name wins inside
Inside check, the local status hides the outer one. The call prints busy. After the function ends, the outer status is still ok. Same spelling, two bindings — scope decides which one you touch.
Show a scoped value on the page
Keep a global label, build a line inside a function that can still read it, and write the result into a paragraph.
<!DOCTYPE html>
<html>
<head>
<title>scope — page</title>
</head>
<body>
<p id="msg">…</p>
<script>
let place = "Garden";
function line() {
let state = "open";
return place + " is " + state;
}
let box = document.getElementById("msg");
box.textContent = line();
</script>
</body>
</html>
The paragraph shows Garden is open. place is global; state is local to line. The function reads both, returns one string, and the page displays it. Change only place, refresh, and watch the line update. Next lesson covers closures — functions that keep outer names alive after the outer call finishes.
Practice steps
- Declare a global
let app = "demo"and log it from a small function and from outside. - Put
let secret = 1inside a function; confirm that loggingsecretoutside throws. - Inside an
if (true) { }block, declarelet n = 5and log it; try loggingnafter the block. - From a nested function, read an outer
letwithout redeclaring it. - Shadow an outer
let tag = "A"with an innerlet tag = "B"and log both places. - In the HTML demo, change only the
placestring, refresh, and read the paragraph.
Frequent slip-ups
- Using a local name outside its function — that is a ReferenceError, not a silent blank
- Expecting
varinside a block to stay private the wayletdoes - Assuming outer code can see names created only inside a nested function
- Mixing scope up with
return— scope is about visibility; return is about sending a value out - Expecting a full closures lesson here — keeping outer names after the call ends is the next page
Summary
- Scope is where a name can be seen and used
- Globals live outside functions and are shared across the script
- Locals and parameters stay inside their function
letandconstinside{ }are block-scoped; prefer them over leakingvar- Nested code may read outward; outer code cannot see inner-only names
- An inner binding with the same name shadows the outer one
- Page updates can combine a global and a local in one returned string
- Next: closures — functions that remember outer names
🧠 Test Your Knowledge
Test Your Knowledge
Challenge yourself with this interactive quiz and see how well you understand the topic
📝 Instructions
- Read each question carefully
- Select the best answer for each question
- You can retake the quiz as many times as you want
- Your progress will be shown at the top