JavaScript Block Scopes, Shadowing, and Beyond.


Hi there, Javascript enthusiasts!
Today, we're exploring the fascinating universe of JavaScript blocks, which are nothing but collections of statements that combine to create compound statements.
Yes, we're talking about Blocks - the unsung heroes of code organization.
What's a Block?
A Block (also known as a compound statement) in JavaScript is like a cozy group for your statements, huddled within curly braces {...}
.
Let's take a peek at a quick example:
{
var a = 10;
let b = 20;
const c = 30;
// Hoisting: let and const are block-scoped, while var is globally hoisted.
}
In this example, let
and const
are hoisted within the block scope, creating a separate memory space called a block.
Block Scope and Accessibility
Example 1:
{
var a = 10;
let b = 20;
const c = 30;
}
console.log(a); // 10
console.log(b); // Uncaught ReferenceError: b is not defined
Why does b
throw a ReferenceError?
Inside the block, let
and const
are hoisted as undefined
in a block-specific memory space, while var
sits comfortably in the global space. This distinction makes let
and const
block-scoped, inaccessible outside the block.
Example 2:
function hoistingExample() {
console.log(a); // undefined
console.log(b); // Uncaught ReferenceError: b is not defined
console.log(c); // Uncaught ReferenceError: c is not defined
var a = 10;
let b = 20;
const c = 30;
console.log(a); // 10
console.log(b); // 20
console.log(c); // 30
}
hoistingExample();
This demonstrates the hoisting behavior of var
, where it gets initialized with undefined
and is accessible throughout the function. On the other hand, let
and const
exhibit block-scoping behavior, only becoming accessible after their declaration within the block.
Shadowing: The Dark Side of Blocks
var a = 100;
{
var a = 10;
let b = 20;
const c = 30;
console.log(a); // 10
console.log(b); // 20
console.log(c); // 30
}
console.log(a); // 10, not 100 as expected
Meet Shadowing! If you name a variable inside a block the same as one outside, it shadows the external variable. Beware, though; this behavior is exclusive to var
.
Let's Talk Functions and Shadowing
const c = 100;
function x() {
const c = 10;
console.log(c); // 10
}
x();
console.log(c); // 100
And for the var
enthusiasts:
var c = 100;
function x() {
var c = 30;
console.log(window.c); // 100
window.c = 20;
}
x();
console.log(c); // 20
Functions have their own scopes, and the same shadowing concept applies here.
Illegal Shadowing
let a = 20; { var a = 20; } // Uncaught SyntaxError: Identifier 'a' has already been declared
Trying to shadow let
with var
results in a syntax error. let
can be shadowed using let
, but var
can be shadowed with either.
The problem is that var
does not respect block scope; it is function-scoped or globally-scoped. As a result, this line is interpreted as an attempt to redeclare the same variable a
in the same scope, leading to a SyntaxError.
Remember, every scope rule that applies to functions also applies to arrow functions.
Visualizing Block Scopes
Each block has its own scope, following lexical scope rules. Everything inside a block, including functions and variables declared with let
and const
, is confined to that block.
In conclusion, understanding block scopes and shadowing is essential for writing clean, error-free JavaScript code.
Thank you!
Subscribe to my newsletter
Read articles from Prerana Nawar directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
