JavaScript Scope
Table of Contents + β
In the previous lesson, we learned how a function can remember the variables it was created with. Now letβs look at the rule that makes that possible: scope, which decides where each variable is accessible.
π What is Scope?
Scope is the part of your code where a variable can be used. A variable created in one place is not always visible everywhere else. Where you declare a variable decides who gets to see it.
JavaScript has three kinds of scope.
| Scope | Where it is created | Who can see the variable |
| Global scope | Outside any function or block | Every part of your code |
| Function scope | Inside a function | Only code inside that function |
| Block scope | Inside a { } block with let or const | Only code inside that block |
π Global Scope
A variable declared outside every function and block lives in the global scope. Any code in the file can read it.
const appName = "Notes"; // global
function showName() { console.log(appName); // β
can see the global variable}
showName(); // Notesconsole.log(appName); // NotesLetβs walk through what this code does:
const appName = "Notes"sits outside every function, so it lives in the global scope.- Inside
showName, the lineconsole.log(appName)reaches up and reads the global variable without any trouble. - The call
showName()printsNotes, and the finalconsole.log(appName)at the top level printsNotestoo, because global variables are visible everywhere.
π¦ Function Scope
A variable declared inside a function belongs to that function. Code outside the function cannot see it.
function greet() { const message = "Hello"; // function-scoped console.log(message); // β
Hello}
greet();console.log(message); // β ReferenceError: message is not definedLetβs walk through what this code does:
const message = "Hello"is declared insidegreet, so it is function-scoped and exists only whilegreetruns.console.log(message)inside the function printsHello, because the variable is visible right where it was created.console.log(message)after the call throws aReferenceError, since outside the function the variable does not exist at all.
π§± Block Scope
A block is any pair of curly braces, such as the body of an if statement or a loop. When you declare a variable with let or const inside a block, it stays inside that block.
if (true) { let count = 1; // block-scoped console.log(count); // β
1}
console.log(count); // β ReferenceError: count is not definedLetβs walk through what this code does:
let count = 1is declared inside theifblock, so it is block-scoped to those curly braces.console.log(count)inside the block prints1, because the variable is alive within the block.console.log(count)after the block throws aReferenceError, since the variable disappears the moment the block ends.
The same applies inside a loop. Each variable declared with let or const stays where it was created.
for (let i = 0; i < 3; i++) { const doubled = i * 2; console.log(doubled); // β
0, 2, 4}
console.log(i); // β ReferenceError: i is not definedLetβs walk through what this code does:
let iis declared in the loop header, so it is scoped to the loop and its body.const doubled = i * 2is created fresh on each pass and lives only inside the loop body, printing0,2, then4.console.log(i)after the loop throws aReferenceError, becauseianddoubledboth vanish once the loop finishes.
var does not respect blocks
let and const are block-scoped, but var is only function-scoped. A var
declared inside an if or loop leaks out to the surrounding function. Use
let and const so your variables stay where you put them.
π The Scope Chain
Inner scopes can read variables from the scopes around them, but outer scopes cannot read variables from inside. JavaScript looks outward, never inward. This outward search is called the scope chain.
const outer = "I am outside";
function inner() { const local = "I am inside"; console.log(outer); // β
inner can read the outer variable}
inner();console.log(local); // β outside cannot read the inner variableLetβs walk through what this code does:
const outerlives in the global scope, whileconst locallives only insideinner.console.log(outer)works insideinner, because the inner scope can look outward along the scope chain to findouter.console.log(local)at the top level throws aReferenceError, since the outer scope cannot look inward into the function.
When code uses a variable, JavaScript first looks in the current scope. If it is not found there, it moves one level out, then keeps moving outward until it reaches the global scope.
π§ How Scope Powers Closures
The closures from the previous lesson work because of scope. When a function is created inside another function, the inner function keeps access to the outer functionβs variables through the scope chain, even after the outer function has finished.
function makeCounter() { let count = 0; // function-scoped
return function () { count++; // β
still reaches the outer variable return count; };}
const next = makeCounter();console.log(next()); // 1console.log(next()); // 2Letβs walk through what this code does:
let count = 0is function-scoped tomakeCounter, and the returned inner function reaches it through the scope chain.const next = makeCounter()runs the outer function once and stores the returned inner function, which still holds its link tocount.- Each
next()call runscount++and returns the new value, printing1then2, because that onecountsurvives between calls.
The returned function still sees count because scope decides what a function can reach, and that link survives after makeCounter returns.
β οΈ Common Mistakes to Avoid
| Mistake | Problem | Solution |
| Using a function variable outside the function | You get a ReferenceError because it is not visible | Return the value, or declare it in an outer scope |
Expecting a let block variable to exist after the block | The variable disappears once the block ends | Declare it before the block if you need it after |
Using var inside a loop or if | It leaks out and can be changed by accident | Use let or const for true block scope |
Assigning without let or const | You create an accidental global variable | Always declare with const or let |
π§ Try It Yourself!
- Declare a global variable and read it from inside a function.
- Declare a variable inside a function and try to read it outside. Watch the
ReferenceError. - Declare a
letvariable inside anifblock and try to use it after the block. - Write an outer and inner function, then confirm the inner one can read the outer variable but not the other way around.
π§© What Youβve Learned
- β Scope is the part of your code where a variable is accessible
- β Global scope is visible everywhere; function scope stays inside the function
- β
Block scope keeps
letandconstvariables inside their{ } - β
letandconstare block-scoped, whilevaris only function-scoped - β Inner scopes can read outer variables through the scope chain, but not the reverse
- β Closures work because scope keeps the link to outer variables alive
π Whatβs Next?
Now that you know where variables live, letβs see how JavaScript moves declarations to the top before your code runs. Letβs continue to Hoisting.