Facebook Pixel

Block Scope and the Lexical Scope Chain

Block scope fits into the lexical scope chain. Here is how blocks create environments and how resolution works.

Block Scope and the Lexical Scope Chain

Blocks create new lexical environments that fit into the scope chain. Understanding this makes block scope and closures click.

Blocks Create Lexical Environments

Every block with let or const declarations creates a new lexical environment. Its outer reference points to the enclosing environment.

const x = 1; { const y = 2; // this block's environment: { y: 2 }, outer -> the environment with x console.log(x, y); // 1 2 } `` ### How Resolution Works in Blocks When the engine needs a variable inside a block: 1. Check the block's environment record. 2. If not found, follow the outer reference to the parent environment. 3. Repeat up to the global environment. ```js const a = 1; function foo() { const b = 2; if (true) { const c = 3; console.log(a, b, c); // 1 2 3, via block -> foo -> global chain } console.log(c); // ReferenceError (c is block-scoped) } foo(); `` The `if` block's environment has `c`. Its outer reference is `foo`'s environment (has `b`). `foo`'s outer is the global (has `a`). ### Blocks Do Not Create Scope for `var` ```js function foo() { if (true) { var x = 1; // x is in foo's scope, not the block's } console.log(x); // 1 (var leaked out of the block but stayed in foo) } foo(); `` `var` goes into the function's variable environment, not a block environment. This is why `var` leaks out of blocks. ### Block Scope and Closures A function defined inside a block closes over the block's environment: ```js function foo() { const fns = []; for (let i = 0; i < 3; i++) { fns.push(() => i); } return fns; } const [a, b, c] = foo(); console.log(a(), b(), c()); // 0 1 2 `` Each iteration's block has its own `i`. Each arrow closes over its block's `i`. This is the `let` loop closure fix. ### `const` in `for...of` ```js const fns = []; for (const item of [1, 2, 3]) { fns.push(() => item); } console.log(fns.map(f => f())); // [1, 2, 3] `` Each iteration creates a new block environment with a fresh `item` binding. `const` works because `item` does not change within an iteration. ### The Takeaway Blocks with `let`/`const` create new lexical environments that fit into the scope chain. The outer reference points to the enclosing environment. `var` does not use block environments (it goes into the function's variable environment). Closures over block-scoped variables work because the block environment is retained.

Yes. Every block with let or const declarations creates a new lexical environment. Its outer reference points to the enclosing environment, forming part of the scope chain.

No. var goes into the function's variable environment, not a block environment. This is why var leaks out of blocks but stays in the function. Only let and const use block environments.

Each block's environment has an outer reference to the enclosing environment. The engine checks the block first, then follows outer references up through function and global environments until the variable is found or ReferenceError is thrown.

A function defined inside a block closes over the block's lexical environment. Even after the block ends, the function retains access to the block's variables. This is how let in a for loop fixes the closure bug.

In for...of, each iteration creates a fresh binding that does not change within the iteration, so const is fine. In a classic for loop, i++ reassigns i, which const does not allow, so it throws TypeError.

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.

Please Login.
Please Login.
Please Login.
Please Login.