JavaScript Block Scopes, Shadowing, and Beyond.

Prerana NawarPrerana Nawar
3 min read

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!

0
Subscribe to my newsletter

Read articles from Prerana Nawar directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Prerana Nawar
Prerana Nawar