Common Block Scope Bugs in JavaScript
Block scope has its own pitfalls. Here are the most common bugs and how to fix them.
Common Block Scope Bugs in JavaScript
Block scope (let and const) is safer than var, but it has its own pitfalls. Here are the common bugs.
Bug 1: TDZ Access
console.log(x); // ReferenceError let x = 5; `` **Fix**: Declare before use. The TDZ is intentional. ### Bug 2: Re-declaration in the Same Scope ```js let x = 1; let x = 2; // SyntaxError `` **Fix**: Use distinct names or reassign (`x = 2`). ### Bug 3: Re-declaration Across `switch` Cases ```js switch (x) { case 1: let y = 1; break; case 2: let y = 2; // SyntaxError (same scope) break; } `` **Fix**: Wrap each case in braces. ```js switch (x) { case 1: { let y = 1; break; } case 2: { let y = 2; break; } } `` ### Bug 4: `const` Without Initializer ```js const x; // SyntaxError `` **Fix**: Always initialize `const`: `const x = 5;`. ### Bug 5: Reassigning `const` ```js const x = 5; x = 10; // TypeError `` **Fix**: Use `let` if you need to reassign. ### Bug 6: `const` in a Classic `for` Loop ```js for (const i = 0; i < 3; i++) {} // TypeError `` **Fix**: Use `let` for the classic `for` counter. ### Bug 7: Expecting `const` to Freeze Objects ```js const obj = { a: 1 }; obj.a = 2; // OK (no error, but maybe unexpected) `` **Fix**: Use `Object.freeze` for immutability. ### Bug 8: Illegal Shadowing ```js let x = 1; { var x = 2; // SyntaxError } `` **Fix**: Use `let` in the block instead of `var`. ### Bug 9: Shadowing + TDZ ```js let x = 1; { console.log(x); // ReferenceError (inner let x is in TDZ) let x = 2; } `` **Fix**: Use distinct names or declare the inner `let` before the log. ### Bug 10: Closure Over a `let` Loop Variable (Actually a Feature) ```js for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); } // logs 0, 1, 2 (this is correct, not a bug) `` This is actually the fix, not a bug. But it confuses people who expect `var` behavior. ### The Takeaway Common block scope bugs: TDZ access, re-declaration, switch case re-declaration (fix with braces), `const` without initializer, reassigning `const`, `const` in classic `for`, expecting `const` to freeze objects, illegal shadowing, and shadowing + TDZ. Use `const`/`let` correctly, wrap switch cases in braces, and use `Object.freeze` for immutability.
TDZ access: using a let or const variable before its declaration line throws ReferenceError. This is intentional (to catch bugs), so the fix is to declare before use.
Because all cases in a switch share one block scope (unless wrapped in braces). A let in two cases is a re-declaration in the same scope. Fix by wrapping each case in its own block with braces.
Because i++ reassigns i, and const does not allow reassignment. Use let for classic for loop counters. const works in for...of and for...in because each iteration is a fresh binding.
Because the inner let x is hoisted (shadowing the outer) and is in the TDZ. It does not fall back to the outer x. Accessing it before the inner declaration line throws ReferenceError.
Use let in the inner block instead of var. You cannot shadow a let, const, or class with a var in the same scope because var leaks out of blocks and would conflict. The reverse (shadowing a var with a let) is legal.
Ready to master React completely?
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.
Master React
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course.

