Free lesson · JavaScript Basic to Advance
Scope: Global, Function, Block
What Is Scope?
Scope is the rule that determines where a variable can be accessed in your code. Every variable you declare lives somewhere — a function, a block, or the global environment — and that place determines who can see it.
const platformName = "AlgoInsight"; // accessible everywhere in this file
function greet() {
const greeting = "Hello"; // accessible only inside greet
console.log(greeting);
console.log(platformName); // ✅ can see outer variables
}
greet();
console.log(platformName); // ✅ accessible
// console.log(greeting); // ❌ ReferenceError — greeting doesn't exist here
platformName is declared at the top level, so it's accessible everywhere. greeting is declared inside greet(), so it only exists while greet is running and only inside that function. Trying to read greeting from outside the function fails — because as far as the outer code is concerned, that variable doesn't exist.
Scope is one of the most important concepts in JavaScript. Every bug related to "variable not found" or "variable has the wrong value" is ultimately a scope question. Once you understand scope deeply, those bugs stop happening to you.
Why Scope Exists
Imagine a programming language where every variable was visible everywhere. A function written by one person could accidentally overwrite a variable used by another part of the code. Helper variables inside loops would leak out and cause conflicts. Large codebases would become impossible to reason about, because any change anywhere could break anything anywhere else.
Scope solves this by giving variables boundaries. Variables declared inside a function are invisible outside it. Variables declared inside a block (an if statement, a for loop) are invisible outside that block. These boundaries are what make it possible to write large programs where different parts don't interfere with each other.
Scope also exists for performance reasons. Because JavaScript knows the boundaries of each variable, it can clean up memory when a function finishes — variables that are no longer accessible can be garbage collected. We touched on this in the closures discussion, and we'll return to it in Module 13.
The Scope Chain
When you read a variable in JavaScript, the engine looks for it using a specific search pattern called the scope chain:
- First, look in the current scope (the function or block you're in right now).
- If not found, look in the surrounding scope (the function or block that contains this one).
- If not found, keep walking outward, one level at a time.
- If you reach the top (the global scope) without finding it, throw a ReferenceError.
const level0 = "I'm at the top level";
function outer() {
const level1 = "I'm in outer";
function inner() {
const level2 = "I'm in inner";
console.log(level2); // found in current scope
console.log(level1); // found in surrounding (outer)
console.log(level0); // found at top level
}
inner();
}
outer();
When the line console.log(level0) runs, JavaScript searches for level0 in inner's scope (not found), then in outer's scope (not found), then at the top level (found, value retrieved). The search stops as soon as the variable is found.
graph TD
A["console.log(level0) runs inside inner()"] --> B["Search inner's scope"]
B --> C{Found?}
C -->|No| D["Search outer's scope"]
D --> E{Found?}
E -->|No| F["Search global scope"]
F --> G{Found?}
G -->|Yes| H["Use that value"]
G -->|No| I["ReferenceError"]
style H fill:#10b981,color:#fff
style I fill:#dc2626,color:#fff
This diagram shows the scope chain search process. When JavaScript needs to read a variable inside a function, it first searches the function's own scope. If the variable isn't found, it searches the surrounding function's scope. It continues walking outward through enclosing scopes until it either finds the variable or reaches the global scope. If the variable isn't found at any level, JavaScript throws a ReferenceError.
The search direction is important: scope only walks outward, never inward. An outer function cannot see variables declared in an inner function. This is the asymmetry that makes scope useful — inner code can use outer values, but outer code can't accidentally see inner ones.
Interactive animation: The lookup walks outward and finds an outer variable, but outer code reading an inner one gets a ReferenceError.
The Three Levels of Scope
Interactive animation: Global, function and block scope as nested boxes - inner scopes see outward, outer scopes never see in.
JavaScript has three kinds of scope. Knowing each one and how they interact is what makes you fluent with variables.
Global scope. Variables declared at the top of your file, outside any function or block, live in global scope. They're accessible from anywhere in the program. In a browser, global variables become properties of the window object. In Node.js, they become properties of global. Global variables are convenient but dangerous — too many globals make code unpredictable.
Function scope. Variables declared inside a function (with any keyword) are local to that function. They exist while the function runs and disappear when the function returns. Each function call creates its own fresh set of local variables.
Block scope. Variables declared inside a block — anything wrapped in curly braces like if, for, while, or a plain { } — with let or const are local to that block. This is the modern behavior added in ES6. var does not respect block scope (covered in Topic 3.3).
const globalVar = "I'm global"; // global scope
function exampleFunction() {
const functionVar = "I'm in the function"; // function scope
if (true) {
const blockVar = "I'm in the block"; // block scope
console.log(globalVar); // ✅ accessible
console.log(functionVar); // ✅ accessible
console.log(blockVar); // ✅ accessible
}
console.log(functionVar); // ✅ accessible
// console.log(blockVar); // ❌ ReferenceError — block scope ended
}
exampleFunction();
console.log(globalVar); // ✅ accessible
// console.log(functionVar); // ❌ ReferenceError — function scope ended
That example shows all three levels in action. The block scope dies when the if block ends. The function scope dies when exampleFunction returns. The global scope persists for the lifetime of the program.
Variable Shadowing
When a variable in an inner scope has the same name as one in an outer scope, the inner variable shadows the outer one. Inside the inner scope, only the inner variable is accessible.
const name = "Olena";
function greet() {
const name = "Nikhil"; // shadows the outer 'name'
console.log(name); // "Nikhil"
}
greet();
console.log(name); // "Olena" — outer variable still intact
The outer name still exists. It's just hidden inside greet() because there's a local name taking precedence. Once greet returns, the local name is gone and the outer name becomes accessible again.
Shadowing is sometimes useful — you might want a parameter named the same thing as an outer variable. But it can also confuse readers of your code. Many style guides discourage shadowing for this reason. When in doubt, use different names for inner and outer variables.
Interactive animation: Same spelling, two different names - the inner one wins inside its scope, the outer one is untouched.
Common Pitfalls
Warning: Three scope traps that catch developers.
Accidentally creating globals. If you assign to a variable without declaring it (
name = "Olena"instead ofconst name = "Olena"), JavaScript creates a global variable. This is almost always a bug. Strict mode (covered in Module 10) prevents this by throwing an error instead.Assuming variables persist beyond their scope. A variable declared inside a function ceases to exist when the function returns. You can't access it from outside, and it gets garbage collected. The exception is closures — when an inner function captures a reference to an outer variable, that variable persists. We cover this in Module 6.
Shadowing without intent. If you use the same variable name in nested scopes, you're shadowing the outer variable. Sometimes this is what you want; often it's an accident. Linters can catch unintentional shadowing if you enable the relevant rule.
Why This Matters in Interviews
Interview: Scope questions are foundational interview territory. Common forms:
- "What is variable scope in JavaScript?"
- "Output prediction" — code with nested functions and shadowed variables, predict what gets logged
- "What happens if you access a variable that doesn't exist?" (Answer: ReferenceError, unless it's
undefined— that case isundefined, not an error)Topic 3.6 — Interview Lens: Variables, Scope, and Hoisting — covers the deeper interview-prep treatment of these questions.
Try It
Exercise: Predict the output of this code, then run it:
const x = "outer x"; function first() { const x = "first's x"; console.log(x); second(); } function second() { console.log(x); } first(); console.log(x);Specifically: what does
second()log? Whensecondruns and readsx, whichxdoes it find —first's or the outer one? Why? The answer reveals one of the most-misunderstood things about scope: scope is determined by where a function is written, not by where it's called from. That principle is the basis for closures, which we cover in Module 6.