Suggest an editImprove this articleRefine the answer for “Lexical environment in JavaScript”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Lexical environment** is an internal JavaScript structure that stores variable bindings for a specific scope and a reference to the outer scope where the code was written. ```javascript let x = 10; function log() { console.log(x); } function run() { let x = 20; log(); } run(); // prints 10, because log reads from its own definition scope ``` **Key:** scope is where the code lives, not where it runs.Shown above the full answer for quick recall.Answer (EN)Image**Lexical environment** is an internal JavaScript structure that links variable names to their values for a specific scope, and stores a pointer to the enclosing scope where the code was written. ## Theory ### TL;DR - Every function, block `{}`, and the global scope gets its own lexical environment when code runs - Each environment has two parts: **Environment Record** (the actual variables) and an **outer reference** (pointer to the parent scope) - Scope is determined by where you write the code, not where you call it - The chain of outer references is the scope chain - Closures work because functions keep a reference to the lexical environment where they were defined ### Quick example ```javascript let city = "Kyiv"; function greet() { let name = "Anna"; console.log(name + " from " + city); // "Anna from Kyiv" } greet(); ``` When `greet` runs, JavaScript creates a lexical environment for it. That environment holds `name = "Anna"` and has an outer reference to the global environment where `city = "Kyiv"` lives. `greet` finds `city` by walking up the chain. ### Two parts of a lexical environment The **Environment Record** stores the actual bindings: variables, function declarations, parameters. Think of it as a key-value map for that scope. The **outer reference** points to the lexical environment of the code that *surrounds* the function in the source file. Not where the function is called. Where it is written. ```javascript function makeCounter() { let count = 0; // stored in makeCounter's Environment Record return function () { count++; // found via outer reference to makeCounter's environment return count; }; } const counter = makeCounter(); counter(); // 1 counter(); // 2 ``` After `makeCounter` returns, its execution context is gone. But the inner function still holds the outer reference to `makeCounter`'s environment. That environment survives because there is a live reference to it. That is a closure. ### Lexical vs dynamic scoping JavaScript uses lexical scoping. Variables are resolved based on where the code is *written*, not where it is *called*. ```javascript let x = 10; function log() { console.log(x); } function run() { let x = 20; log(); // prints 10, not 20 } run(); ``` `log` was defined in the global scope. Its outer reference points to the global environment. The `x = 20` inside `run` is invisible to `log`, because `log` does not look there. Languages like Perl and Bash can use dynamic scoping, where variable lookup follows the call stack. JavaScript does not. ### How the engine builds the chain When a function is *created* (not called), the engine attaches the current lexical environment to it as `[[Environment]]`. When the function is *called*, a new lexical environment is created for that call, and its outer reference is set to whatever is stored in `[[Environment]]`. That is why scope always follows the definition site. ### Common mistakes **Confusing lexical environment with execution context.** Execution context is the runtime record of a function call. Lexical environment is one component of that context. Related, but not the same thing. **Assuming scope follows the call stack.** Developers often expect `log()` called from inside `run()` to see `run`'s variables. It does not. The chain is fixed at definition time. **Forgetting that `var` skips block environments.** `let` and `const` create bindings in the block's environment record. `var` ignores block environments and binds to the nearest function environment. ```javascript { let blockVar = "block-scoped"; var funcVar = "function-scoped"; } console.log(typeof blockVar); // "undefined" - not accessible outside the block console.log(funcVar); // "function-scoped" ``` **Thinking the environment is destroyed when the function returns.** The garbage collector removes it only when no references remain. A closure that is still alive keeps the environment alive. ### Real-world usage - **React hooks**: `useState`, `useCallback`, and `useEffect` closures capture values from the lexical environment when they are created. The stale closure bug in `useEffect` - where the callback reads an outdated value - is probably the most common issue I see when teams move away from class components - **Module pattern**: IIFE-based modules keep private state in a closed-over lexical environment - **Event handlers in loops**: the classic `var` + `setTimeout` bug happens because all handlers share the same environment and read `i` after the loop finishes - **Memoization**: `useMemo` and manual memoize functions rely on closures over a cached result ### Follow-up questions **Q:** What is the difference between lexical environment and scope chain? **A:** The scope chain is the chain of outer references between lexical environments. The lexical environment is the individual node. The chain is what JavaScript traverses when looking up a variable. **Q:** When is a lexical environment created? **A:** A new one is created every time a function is called or a block is entered. Recursive calls each get their own, so they do not overwrite each other. **Q:** Can a lexical environment outlive the function call that created it? **A:** Yes. If an inner function closes over it, the environment stays alive as long as that inner function is reachable. This is the mechanism behind closures. **Q:** What happens to `let` variables before the line where they are declared? **A:** They exist in the environment record but are uninitialized. Accessing them before the declaration throws a `ReferenceError`. This range is called the Temporal Dead Zone (TDZ). **Q:** How does `var` behave differently from `let` regarding lexical environments? **A:** `var` declarations are hoisted to the nearest function's environment record, not the block's. They are also initialized to `undefined` immediately, unlike `let` and `const`. ## Examples ### Closure keeping the environment alive ```javascript function makeAdder(x) { // x lives in makeAdder's Environment Record return function (y) { return x + y; // x is found via outer reference }; } const add5 = makeAdder(5); const add10 = makeAdder(10); console.log(add5(3)); // 8 console.log(add10(3)); // 13 ``` `makeAdder` was called twice, so two separate environments exist, each with its own `x`. `add5` and `add10` are closures over different environments. That is why they produce different results from the same input `3`. ### The var-in-loop problem and the fix with let ```javascript // Bug: all callbacks share one environment, i = 3 by the time they fire for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 100); // prints 3, 3, 3 } // Fix: let creates a fresh block environment each iteration for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 100); // prints 0, 1, 2 } ``` With `var`, there is one binding in the enclosing function's environment. All three callbacks point to the same `i`. With `let`, the loop creates a fresh block environment each iteration with its own `i`, so each closure captures something different.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.