Q101: Lexical Environment and Environment Records
What Interviewers Want To Evaluate
This question tests whether you understand how JavaScript resolves identifiers at runtime. Interviewers want lexical environments, environment records, outer references, global environment behavior, function environments, block scope, temporal dead zone, and how this model explains closures.
Senior engineers should be able to explain scope without hand-waving around “variables are stored somewhere.”
Short Interview Answer
A lexical environment is the runtime structure JavaScript uses to store bindings and resolve names. It has an environment record, which stores bindings such as let, const, var, function declarations, and parameters, plus an outer reference to the parent lexical environment. When code reads an identifier, JavaScript checks the current environment record and then walks outward through outer references until it finds the binding or reaches the global environment. This model explains scope, closures, block scope, and temporal dead zone behavior.
Detailed Interview Answer
JavaScript scope is lexical, meaning it is mostly determined by where code is written, not where a function is called. The engine creates lexical environments as code enters scripts, modules, functions, and blocks.
The useful mental model is:
Lexical Environment
Environment Record
bindings for identifiers
Outer Reference
link to parent lexical environment
Environment Records
An environment record stores bindings. A binding is the association between an identifier name and a value or storage location.
Different environment records exist for global code, functions, modules, and objects. For interview purposes, the important point is that JavaScript does not simply search random objects when resolving lexical variables. It follows lexical environment links.
Function Environments
When a function is called, JavaScript creates a function execution context and a lexical environment for that call. Parameters and local declarations live there.
Each call gets its own environment. This is why recursive calls do not overwrite each other’s local variables.
Block Scope
let and const are block scoped. Blocks such as if, for, and standalone braces can create lexical environments.
var is function scoped, so it behaves differently and is not bound to the nearest block in the same way.
Temporal Dead Zone
The temporal dead zone is the period where a let or const binding exists but cannot be accessed before initialization.
This is why typeof someLet can throw if someLet is in the temporal dead zone.
Identifier Resolution
When JavaScript sees an identifier, it checks the current lexical environment. If the binding is not found, it checks the outer environment, and continues until the global environment.
If no binding is found, a ReferenceError occurs in strict contexts or reads.
Common Mistakes
A common mistake is saying closures copy variables. Closures do not copy values by default. They keep access to the lexical environment where the variable binding lives.
Another mistake is confusing lexical scope with object property lookup. Scope chain and prototype chain are different mechanisms.
Senior Trade-Offs
Understanding lexical environments helps debug stale closures, accidental globals, loop callbacks, module state, and memory retention.
Senior engineers use this model when explaining bugs, not only language trivia.
Code or Design Example
const label = "global";
function outer() {
const label = "outer";
function inner() {
console.log(label);
}
return inner;
}
const run = outer();
run(); // "outer"
inner resolves label through the lexical environment from outer, even after outer has returned.
Production Example
In React, callbacks often close over values from a render. If an effect or event handler uses an old closure, it may read stale state.
The bug is easier to understand when you remember that the function keeps access to the lexical environment from the render where it was created.
Debugging Workflow
When debugging a scope issue, ask:
Where is the identifier declared?
Which function or block created the binding?
Which closure holds the binding?
Is the value stale or the binding missing?
Is this scope chain or object property lookup?
Revision Notes
Lexical environments store bindings and point to outer environments. Identifier lookup follows this chain.
Key Takeaways
Lexical environments are the foundation for scope, closures, block behavior, and many JavaScript interview questions.
Follow-Up Questions
- What is a lexical environment?
- What is an environment record?
- How does identifier lookup work?
- How do
letandconstdiffer fromvar? - What is temporal dead zone?
- Why do recursive calls get separate local variables?
- How do closures relate to lexical environments?
- How is scope chain different from prototype chain?
- How does this explain stale closures?
- How does module scope differ from script scope?
How I Would Answer This In A Real Interview
I would explain that a lexical environment is a runtime structure containing an environment record and an outer reference. Then I would walk through identifier lookup and use closures, block scope, and temporal dead zone as examples.