Suggest an editImprove this articleRefine the answer for “What is closure in js?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Closure** - a function that retains access to outer scope variables even after the outer function has returned. ```javascript function createCounter() { let count = 0; return () => ++count; } const counter = createCounter(); counter(); // 1 counter(); // 2 - count persists ``` **Key point:** each closure instance gets its own copy of outer variables, enabling private state without classes.Shown above the full answer for quick recall.Answer (EN)Image**Closure** - a function that keeps access to variables from the scope where it was created, even after that scope has finished executing. ## Theory ### TL;DR - Think of it as a backpack: the inner function packs variables from its birth environment and carries them across calls. - The outer function returns, but its variables stay alive inside the returned function. - Each closure instance gets its own copy of those variables, so two counters never share state. - Use it for private mutable state without a class. Skip it for stateless utilities. ### Quick Example ```javascript function createCounter() { let count = 0; // packed into the "backpack" return function() { count++; console.log(count); }; } const counter = createCounter(); counter(); // 1 counter(); // 2 - count persists between calls ``` `createCounter` has already returned, but `count` is still there. The returned function captured it at creation time. ### Key Difference Global variables solve the same "remember something" problem but pollute the whole namespace. Parameters reset on every call. Closures give you persistent private state tied to one specific function instance. Call `createCounter()` twice and you get two independent counters, each tracking its own `count`. ### When to Use - Private counters and state: `createCounter()` returns a function with hidden, persistent state. - Event handlers: `makeHandler(id)` captures `id` so the callback always knows which element triggered it. - Module pattern: `createLogger(level)` filters logs by a captured level, no class needed. - React hooks: `useEffect` and `useCallback` use closures to capture state and props between renders. Avoid closures when the function is stateless. Just pass arguments directly. ### How JavaScript Handles This V8 and other engines allocate a context object when the inner function is created. Variables from the outer scope are stored there by reference, not copied by value. When the inner function runs later, it looks them up through a context chain (lexical scoping). That context stays in memory until the closure itself becomes unreachable. ### Common Mistakes **Var in loops - the classic trap:** ```javascript for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); // prints 3, 3, 3 } // Fix with let - each iteration gets its own binding: for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); // prints 0, 1, 2 } ``` With `var`, all three callbacks share one `i`. By the time they run, the loop has finished and `i` is 3. I've seen this exact pattern in older jQuery click-binding code where every button handler logged the same index. **Thinking outer variables die after return:** ```javascript function makeAdder(x) { return y => x + y; // x is captured, not dead } const add5 = makeAdder(5); console.log(add5(3)); // 8 - x is still alive ``` `x` lives as long as `add5` does. **Memory leak from DOM listeners:** ```javascript function attach(el) { const bigData = new Array(1000000).fill('x'); // captured by closure el.addEventListener('click', () => console.log(bigData.length)); // bigData cannot be freed until el is removed from DOM } ``` The closure holds a reference to `bigData`, blocking garbage collection. Use only what you need inside the handler, or remove the listener when done. ### Real-World Usage - React: `useCallback` and `useEffect` capture state and props for stable, up-to-date handlers. - Lodash: `_.debounce(fn, ms)` returns a closure tracking the timestamp of the last call. - Express: middleware factories like `auth(userId)` capture `userId` for the request handler scope. - Node.js: `EventEmitter.on('event', handler)` where `handler` closes over listener-specific state. ### Follow-Up Questions **Q:** What happens to memory when a closure holds a large object? **A:** The object stays in memory as long as the closure is reachable. If that closure is attached to a DOM element that never gets removed, you have a leak. Chrome DevTools heap snapshots show which closures are keeping objects alive. **Q:** What is the difference between closure scope and block scope? **A:** Block scope (`let`, `const`) creates a new binding per block or loop iteration. Closure scope spans the entire lifetime of the function and persists across multiple calls to the returned inner function. **Q:** How do you implement private fields without ES2022 class fields? **A:** Use a closure inside the constructor: `class Counter { constructor() { let count = 0; this.inc = () => ++count; } }`. Each instance gets its own `count` through its own closure. **Q:** Why does `useEffect` in React sometimes read stale state? **A:** The effect captures variables at the time it runs. If state changes after that, the closure still holds the old value. Add the variable to the dependency array to get a fresh closure, or use `useRef` for values you need to read without re-running the effect. ## Examples ### Basic: Private State with a Counter ```javascript function createCounter() { let count = 0; return { increment() { count++; }, decrement() { count--; }, value() { return count; } }; } const c = createCounter(); c.increment(); c.increment(); c.decrement(); console.log(c.value()); // 1 // count is not accessible from outside: console.log(c.count); // undefined ``` Three methods share one `count`. Nothing outside `createCounter` can read or modify it directly. This is the module pattern that existed before ES modules were standardized. ### Intermediate: Event Handler with Captured Context ```javascript function setupButtons(labels) { labels.forEach((label, index) => { const button = document.createElement('button'); button.textContent = label; // Each callback closes over its own label and index button.addEventListener('click', () => { console.log(`Clicked: ${label} at position ${index}`); }); document.body.appendChild(button); }); } setupButtons(['Home', 'About', 'Contact']); // Clicking "About" logs: Clicked: About at position 1 ``` Because `forEach` gives each callback its own `label` and `index` bindings, every handler correctly knows which button it belongs to. This is the real-world scenario where the `var`-in-a-loop bug used to cause problems before `let` arrived.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.