Glossary / Lexical scope
Lexical scope
Lexical scope determines which bindings an identifier can refer to based on the program’s written nesting structure. JavaScript searches the current lexical environment first, then enclosing environments, until it finds a binding or reaches the outer boundary. Function calls do not dynamically change where a function’s free variable is resolved; its source location establishes the scope chain.
Name resolution and shadowing
const theme = "light";
function render() {
const label = "Menu";
return `${label}: ${theme}`;
}
render can read both its local label and outer theme. If it declares another theme, that local binding shadows the outer one. Shadowing can be useful for narrow scopes, but excessive reuse of names makes reviews and debugging harder.
Lexical scope is distinct from the prototype chain: lexical environments resolve identifiers such as theme; prototypes resolve object properties such as item.name. Modules create their own top-level lexical scope instead of placing imports and declarations on the global object. var has function/global scope; let, const, and class declarations are block-scoped.
Closures preserve access to outer lexical bindings after the outer function returns. They retain bindings, not necessarily snapshots. If the binding changes, later closure calls can observe the new value. This is powerful for encapsulation but can extend the lifetime of objects captured by long-lived callbacks.
Engineering use
Keep state as local as possible, make dependencies explicit, and avoid accidental globals. Use modules for ownership boundaries and pass dependencies as arguments when that makes behavior easier to test. Review shadowing and closure capture in async handlers.
Interview questions
- What determines where a free variable is found? Lexical nesting where the function is defined.
- Is scope lookup the same as prototype lookup? No; one resolves identifiers, the other object properties.
- What does a closure retain? Access to lexical bindings in its captured environment.
- Why use module scope? It prevents top-level declarations from implicitly becoming global bindings and makes dependencies explicit.