Scope in JavaScript: Block, Function, and Shadowing
Ever wondered how JavaScript knows which variable you're referring to, especially when names are reused in different parts of your code? This boils down to the concept of scope. Let's explore how block scope
, function scope
, and a related idea called shadowing
work in JS.
๐ค What is a Block?
You've definitely seen curly braces {}
in JavaScript, especially with if
statements, for
loops, or just on their own.
These curly braces define a block. Think of a block as a way to group multiple JavaScript statements together where JavaScript might otherwise expect only one. It's also known as a compound statement.
Why group statements?
Consider the if
statement. Syntactically, it expects a single statement to execute if the condition is true. Blocks allow us to execute multiple statements conditionally.
if (true) {
// Compound Statement (Block)
var a = 10; // We'll discuss 'var' vs 'let'/'const' soon!
console.log(a);
}
๐ช What is Block Scope?
Now, why are blocks important for variables? Because they create a scope. Block scope determines the accessibility of variables declared inside that block. Specifically, variables declared with let
and const
are block-scoped.
Let's see what happens when we declare variables with var
, let
, and const
inside a block and try to access them both inside and outside.
Console Output:
Inside Block a= 10
Inside Block b= 20
Inside Block c= 30
Outside Block a= 10
Uncaught ReferenceError: b is not defined
What's happening here?
let b
andconst c
are block-scoped. They exist only within the{}
block. Trying to access them outside results in aReferenceError
. They live in a special memory space associated with the block.var a
, however, is not block-scoped (it's typically function-scoped or globally-scoped). It "escapes" the block and becomes accessible outside. In this case, it attaches to the global scope.

Info
This is a key reason why let
and const
(introduced in ES6) are generally preferred over var
โ they limit the variable's "lifespan" to the block where they are needed, reducing potential bugs.
๐ญ What is Shadowing?
What if you declare a variable inside a block with the same name as a variable declared outside the block? This is called shadowing. The inner variable temporarily "hides" or "shadows" the outer variable within the block's scope.
Let's see how var
, let
, and const
behave with shadowing.
Scenario 1: Shadowing var
with var
Console Output:
Because var
is not block-scoped, both var a = 12
and var a = 10
refer to the same variable in the outer (in this case, global) scope. The inner declaration shadows the outer one and modifies its value.
Scenario 2: Shadowing let
with let
Console Output:
This is different! The inner let b = 20
creates a completely new variable b
that only exists within the block. It shadows the outer b
inside the block, but doesn't affect it. Once the block finishes, the inner b
disappears, and the outer b
(value 12) is accessible again.

Scenario 3: Shadowing const
with const
const
behaves identically to let
regarding shadowing and block scope.
Console Output:
Again, the inner const c
is a separate variable confined to the block scope.

๐๏ธ Does Shadowing Work the Same in Functions?
Yes! Functions also create their own scope (function scope). Variables declared inside a function can shadow variables from outer scopes, similar to how let
and const
work in blocks.
index.js | |
---|---|
Console Output:
The c
inside myFunction
is distinct from the global c
.
Illegal Shadowing
While shadowing with let
and const
is predictable, mixing them with var
can lead to errors.
Case 1: Shadowing let
with var
(Illegal)
Console Output:
This is illegal shadowing. You cannot shadow a let
(or const
) variable with var
within the same block scope.
Why is this illegal?
Remember, var
is not truly block-scoped. A var a = 10
inside the block tries to declare a variable in the outer scope (function or global) where let a
already exists. Since let
variables cannot be re-declared in the same scope, this causes a SyntaxError
. var
tries to "cross the boundary" of its block, interfering with the outer let
.
Case 2: Shadowing var
with let
(Legal)
var a = 20;
{
let a = 10; // Perfectly fine!
console.log("Inside block a =", a); // Output: 10
}
console.log("Outside block a =", a); // Output: 20
This works because the inner let a
creates a new, block-scoped variable. It doesn't interfere with the outer var a
.
Important Note on Functions: The illegal shadowing rule applies within the same block context. If the var
is inside a function within the block, it's okay because the function creates its own separate scope:
shadowing.js | |
---|---|
๐งช Practice Time!
Add debugger;
statements or use your browser's developer tools to inspect the scopes for these examples:
Example 1: Nested Blocks
Example 2: Lexical Scope
shadowing-1.js | |
---|---|
Answer
These examples also demonstrate the Lexical Scope chain: JavaScript looks for a variable in the current scope, then in the outer scope, then the next outer scope, and so on, until it finds it or reaches the global scope.
Understanding block scope, function scope, and shadowing (especially the differences between var
, let
, and const
) is crucial for writing predictable and bug-free JavaScript code!