JavaScript ES2023 ·
✓ verified by execution on 2026-08-11
Scope determines the visibility of values and expressions in your code. Understanding scope is essential for managing variables properly and avoiding hard-to-track bugs.
Block Scope
In JavaScript, any pair of curly braces {} forms a block. Variables declared with let or const inside a block are block-scoped, meaning they only exist within those curly braces.
javascript
{ let x = 'hello'; console.log(x);}console.log(typeof x);
Output
hello
undefined
Your output
If you try to access a block-scoped variable from outside its block, JavaScript throws a ReferenceError because the variable is “not available for use”:
javascript
if (true) { let b = 10;}try { console.log(b);} catch (e) { console.log(e.name);}
Output
ReferenceError
Your output
The Scope Chain
Scopes can be layered in a hierarchy. When you nest blocks, you create a parent-child relationship. Child scopes have access to parent scopes, but not vice versa.
javascript · visualize
let globalVar = 1;{ let innerVar = 2; console.log(globalVar + innerVar);}
3
Visualizing Scope Hierarchy
This diagram shows how block scopes nest inside the global scope. Child scopes can read values from their parents, but parents cannot reach into child scopes.
Nested scopes: code inside a block can read variables from the global scope around it, but a variable declared inside the block is not visible outside it.
When JavaScript looks for a variable, it starts in the current scope. If it doesn’t find it, it moves up to the parent scope, continuing until it reaches the global scope. This process is called traversing the scope chain.
Variable Shadowing
Because of how the scope chain works, an interesting effect called shadowing can happen. If you declare a variable in an inner scope with the exact same name as a variable in an outer scope, the inner variable “shadows” (hides) the outer one.
Predict the outputjavascript
Read the code. What exactly will it print? Commit to an answer before you look.
let a = 1;{ let a = 2; console.log(a);}console.log(a);
Output
2
1
You predicted
Re-declaring a variable in an inner scope does not permanently change the outer variable. It only hides it while execution is inside the inner scope. Once the inner scope exits, the outer variable becomes visible again.
Functions also create scopes and follow the exact same rules. A function parameter will shadow an outer variable of the same name:
javascript
function f(a) { { let a = 2; console.log(a); } console.log(a);}f(1);
Output
2
1
Your output
Edge Case: Temporal Dead Zone (TDZ)
When you shadow a variable, you must be careful not to access the outer variable inside the inner block before the inner variable’s declaration. Doing so results in a ReferenceError due to the Temporal Dead Zone.
javascript
let a = 1;{ try { console.log(a); let a = 2; } catch (e) { console.log(e.name); }}
Output
ReferenceError
Your output
The inner block “knows” that a is going to be declared, so it shadows the outer a immediately. But since the let a = 2 line hasn’t run yet, accessing it throws an error.
Check yourself
What happens if you declare a variable with let inside an if block and try to log it outside?
Reveal answer
It throws a ReferenceError — Variables declared with let and const are strictly block-scoped and disappear when the block ends. Attempting to access them outside the block throws a ReferenceError.
Does redeclaring a variable in an inner scope permanently change the outer variable?
Reveal answer
No, it only hides the outer variable while inside the inner scope — This is called variable shadowing. It only hides the outer variable while in the inner scope; the outer variable regains visibility once the inner scope exits.
Can a parent scope access a variable declared in a child scope?
Reveal answer
No, child scopes have access to parent scopes, but not vice versa — Scopes can be layered in a hierarchy. Child scopes have access to parent scopes, but parent scopes cannot reach into child scopes.
Challenges
Challenge 1 +15 XP
Fix the code so that the outer variable x is printed as 1, and the inner variable x is printed as 2.
javascript
Test 1 — expects "2\n1\n"
Need a hint? (−25% XP)
Use the let keyword to redeclare the variable in the inner block.
Show solution (0 XP)
let x = 1;{ let x = 2; console.log(x);}console.log(x);
Challenge 2 +15 XP
Make the code run without a ReferenceError by moving the variable declaration to the correct scope.
javascript
Test 1 — expects "Success\n"
Need a hint? (−25% XP)
Declare the variable outside the block so it is accessible where it is logged.
Show solution (0 XP)
let message;if (true) { message = 'Success';}console.log(message);