Leaderboard
Javascript Jun 30, 2026
Javascript Const Block Scope Shadowing

JavaScript Block Scope — Can you reassign a const?

This is a daily Javascript challenge from the CodeShot archive. Practice your knowledge of Const Block Scope Shadowing and improve your technical interview readiness.

const x = 1
{
  const x = 2
  console.log(x)
}
console.log(x)
A SyntaxError — cannot redeclare const
B 2 then 1
C 2 then 2
D 1 then 1

Detailed Explanation

Why This Question Matters

If you've been writing JavaScript for a while, you know that const is the go-to for declaring variables that shouldn't change. But there is a common misconception that const means "this variable can never be changed anywhere in the script."

That's not actually how it works.

The confusion usually stems from the difference between reassignment and shadowing. Many developers assume that if they see a variable name they've already used, they are modifying the original. In reality, JavaScript handles scope in a way that allows you to "hide" an outer variable with a new one of the same name inside a block. Understanding this is the difference between debugging a weird state bug for three hours and knowing exactly how the engine is treating your data.

Understanding the Code

Let's look at the snippet:

const x = 1
{
  const x = 2
  console.log(x)
}
console.log(x)

At first glance, this looks like it should throw a TypeError. After all, we declared x as a constant, and then we tried to set it to 2. But if you run this in a console, it doesn't crash. It prints 2, then 1.

Here is what's actually happening under the hood.

JavaScript uses block scoping for let and const. A block is anything wrapped in curly braces {}. This could be an if statement, a for loop, or just a standalone pair of braces like we have here.

When the engine enters that block, it creates a new lexical environment. When you write const x = 2 inside those braces, you aren't changing the x from the outside. You are creating a completely new variable that just happens to share the same name. This is called shadowing.

The inner x shadows the outer x for as long as the code is executing inside that block. Once the execution hits the closing brace }, the inner x is garbage collected (or at least goes out of scope), and the outer x becomes visible again.

Finding the Correct Answer

The correct answer is Option B (which would show the output as 2 then 1).

Why not the other options?

1. It's not a TypeError: You aren't reassigning the original x. Reassignment looks like x = 2. Here, we used the const keyword again, which signals a new declaration.
2. The outer x isn't mutated: Because of the block scope, the x = 1 remains untouched.

If the code had been written without the curly braces, it would have looked like this:

const x = 1;
const x = 2; // SyntaxError: Identifier 'x' has already been declared

That's the key. The braces create a boundary. Inside that boundary, you can declare whatever you want, even if the names clash with the outside world.

Common Mistakes Developers Make

The biggest mistake is confusing const with "immutability."

People often think const makes the *value* immutable. It doesn't. It makes the *binding* immutable. If x was an object or an array, you could still change its properties. But in this specific challenge, the trap is thinking that the scope is global or function-wide.

Another common trip-up is assuming var behaves the same way. If you replace const with var, the behavior changes completely because var does not recognize block scope—it only recognizes function scope. With var, the second declaration would simply overwrite the first, and you'd get 2 and 2.

Real-World Usage

You might be wondering, "Why would I ever actually do this in a real project?"

Honestly, shadowing is often discouraged because it makes code harder to read. If you have five different variables named x or data in nested scopes, you'll eventually lose track of which one you're referencing.

However, this concept shows up constantly in production code, even if we don't do it intentionally. Think about for loops:

const userCount = 10;
for (let i = 0; i < 5; i++) {
  // 'i' is scoped only to this block
}

The i inside the loop is a block-scoped variable. If you had another i outside the loop, the loop's i would shadow it.

You also see this in complex middleware or nested closures where a developer might use a generic name like err or res. If a nested function declares its own err, it shadows the one from the parent function. If you don't understand shadowing, you'll spend an hour wondering why your error logger is printing the wrong object.

Key Takeaways

- Block Scope is King: let and const are scoped to the nearest pair of curly braces {}.
- Shadowing $\neq$ Reassignment: Declaring a variable with the same name inside a block creates a new reference; it doesn't overwrite the old one.
- The Binding is Constant: const prevents you from changing which value a name points to within its own scope, but it doesn't stop you from creating a new name in a different scope.
- Avoid it when possible: While shadowing is a language feature, using the same name for different things in the same file is a recipe for confusion. Give your variables unique, descriptive names.

Why this matters

Understanding Const Block Scope Shadowing is crucial for passing technical interviews. In real-world applications, this concept often leads to subtle bugs if not handled correctly. For more details, you can always refer to the official MDN Documentation.

📝
Reviewed by CodeShot Editorial
Every challenge is code-reviewed by senior developers to ensure accuracy and real-world relevance. Learn more.

Ready for your shot?

Join thousands of developers solving one logic puzzle every morning.

Solve Today's Challenge →