Suggest an editImprove this articleRefine the answer for “Closures in JavaScript”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Closure** is a function that retains access to variables from the scope where it was created, even after that scope has finished executing. ```javascript function createCounter() { let count = 0; return () => ++count; } const counter = createCounter(); counter(); // 1 counter(); // 2 ``` **Key point:** `count` stays alive because the returned function holds a reference to it. Once that reference is gone, the variable can be collected.Shown above the full answer for quick recall.Answer (EN)Image**A closure** is a function that retains access to variables from the scope where it was created, even after that scope has finished executing. ## Theory ### TL;DR - **Analogy:** A closure is like a backpack. When a function is created, it packs the variables it needs from the surrounding scope and carries them along. - **Core mechanic:** JavaScript attaches an internal `[[Scope]]` reference to every function, pointing to the lexical environment where the function was defined. Variables referenced through this link stay alive as long as the function exists. - **Every function is technically a closure.** The real question is whether you are using this behavior on purpose. - **Decision rule:** Private state, factory functions, callbacks with context - closures. Three levels of nesting just to pass one value - reach for a class or plain object. ### Quick example ```javascript function createCounter() { let count = 0; // This variable is "captured" return function increment() { count++; return count; }; } const counter = createCounter(); console.log(counter()); // 1 console.log(counter()); // 2 console.log(counter()); // 3 // count is NOT accessible out here - it is private ``` `createCounter` runs once and returns `increment`. The outer function is done. But `count` survives because `increment` still holds a reference to it. Every call to `counter()` reads and updates the same variable. ### Key difference Every function in JavaScript forms a closure over its [lexical scope](/questions/scope-in-javascript). What actually matters in interviews and in production is recognizing when you are intentionally using the captured scope. Writing `setTimeout(() => doSomething(userId), 100)` inside a React component? That callback closes over `userId`. Most developers write closures constantly without labeling them as such. ### When to use - **Private state:** Variables you want to hide from the outside world - counters, config, internal caches - **Factory functions:** `createMultiplier(2)` bakes `2` into a new function without exposing it as a parameter on every call - **Callbacks and event handlers:** `fetch`, `addEventListener`, Promise chains - all capture variables from the surrounding context - **Memoization and debounce:** A timer ID or a cache object lives between calls, stored in a closure without polluting any outer scope Avoid closures when a plain class or object literal would be easier to read. Three levels of nesting to pass a single value is a refactor signal, not a design pattern. ### How JavaScript keeps variables alive When a function is created, the V8 engine in Chrome and Node.js stores an internal `[[Scope]]` property on it. This property points to the lexical environment where the function was defined, including all variables in that scope and every parent scope above it. The [garbage collector](/questions/garbage-collection-javascript) keeps a variable alive as long as at least one reference to it exists. A closure holds that reference. Once you lose the last reference to the closure itself, the function and its captured variables become eligible for collection. ### Common mistakes **Mistake 1: `var` in a loop** ```javascript // Wrong - all callbacks see the same i for (var i = 0; i < 3; i++) { setTimeout(function() { console.log(i); // Logs 3, 3, 3 }, 1000); } // Right option 1 - let creates a new binding per iteration for (let i = 0; i < 3; i++) { setTimeout(function() { console.log(i); // Logs 0, 1, 2 }, 1000); } // Right option 2 - IIFE captures the value before the loop moves on (pre-ES6) for (var i = 0; i < 3; i++) { (function(j) { setTimeout(function() { console.log(j); // Logs 0, 1, 2 }, 1000); })(i); } ``` With `var`, all three callbacks close over the same `i`. The loop finishes before any callback fires, so they all print `3`. With `let`, each iteration creates a new binding. Three separate variables, three separate closures. **Mistake 2: Capturing large objects by accident** ```javascript // Wrong - largeData stays in memory as long as the listener exists function setupListener() { const largeData = new Array(1000000).fill('data'); document.getElementById('btn').addEventListener('click', function() { console.log(largeData.length); // Captures the whole array }); } // Right - capture only the value you actually need function setupListener() { const largeData = new Array(1000000).fill('data'); const length = largeData.length; document.getElementById('btn').addEventListener('click', function() { console.log(length); // Captures a number, not the array }); // largeData is now eligible for garbage collection } ``` **Mistake 3: `this` is not captured by a closure** ```javascript // Wrong - this inside the callback is not obj const obj = { count: 0, increment: function() { setTimeout(function() { this.count++; // this is window or undefined in strict mode }, 1000); } }; // Right - arrow function inherits this from the enclosing scope const obj = { count: 0, increment: function() { setTimeout(() => { this.count++; // this is obj console.log(this.count); // 1 }, 1000); } }; ``` Closures capture variables. `this` is not a variable in the usual sense - its value is determined by how the function is called, not where it was defined. [Arrow functions](/questions/arrow-functions-javascript) have no `this` of their own and fall back to the enclosing scope's `this`, which is why they work here. **Mistake 4: Stale closure in React hooks** ```javascript // Wrong - count is captured at mount and never updates inside the interval function Counter() { const [count, setCount] = React.useState(0); React.useEffect(() => { const timer = setInterval(() => { setCount(count + 1); // count is always 0 here }, 1000); return () => clearInterval(timer); }, []); return <div>{count}</div>; } // Right - use the updater form of setCount function Counter() { const [count, setCount] = React.useState(0); React.useEffect(() => { const timer = setInterval(() => { setCount(prev => prev + 1); // prev is always the latest value }, 1000); return () => clearInterval(timer); }, []); return <div>{count}</div>; } ``` The interval callback closes over `count` at the time the effect runs. With an empty dependency array that is once at mount, so `count` is `0` forever inside the callback. This is the stale closure issue that shows up most often in React-focused interviews - worth having a clean explanation ready. The updater function form bypasses it entirely: React passes the current state as an argument, so you never need to capture it. ### Real-world usage - **React hooks:** `useState`, `useCallback`, and `useRef` are built on closures to persist values between renders - **Redux middleware:** Wraps `dispatch` in a closure to intercept actions before they reach the reducer - **Express middleware:** Route handlers close over the `app` instance, database connections, and configuration - **Debounce and throttle:** Store a timer ID between calls without touching any outer scope - **Module pattern (pre-ES6):** IIFEs created private variables before `import/export` existed - **Dependency injection:** `createUserService(db)` returns functions that close over the database connection ### Follow-up questions **Q:** Why doesn't the captured variable get garbage collected when the outer function returns? **A:** The variable is still referenced by the returned function. The garbage collector keeps objects alive as long as any reference to them exists. Once you discard the closure, the variable is released too. **Q:** Can a closure modify a captured variable, or only read it? **A:** It can modify it. The closure holds a reference to the actual variable binding, not a snapshot. If two closures capture the same variable, they both see each other's writes. **Q:** What is the difference between a closure and passing arguments? **A:** Closures capture variables at creation time and retain them between calls. Arguments are passed fresh at each call. Use closures for state that needs to persist invisibly. Use arguments for dependencies that should be explicit at the call site. **Q:** Can closures cause memory leaks? How do you prevent them? **A:** Yes. If a closure captures a large object and the function is never collected (a DOM listener that is never removed, for example), the object stays in memory. Fix it by removing listeners when done, capturing only primitives or small values, and using WeakMap for closure-based caching. **Q:** (Senior level) Explain why closures in `setTimeout` inside loops behave unexpectedly with `var`. **A:** Closures capture variables by reference. When the timeout fires, the loop has already finished and `i` holds its final value. The callback reads that final value, not the value at the time the timeout was registered. With `let`, each iteration produces a separate variable in a new block scope, so each closure captures a different binding entirely. ## Examples ### Basic: factory function ```javascript function createMultiplier(multiplier) { // multiplier is captured in the closure return function(number) { return number * multiplier; }; } const double = createMultiplier(2); const triple = createMultiplier(3); console.log(double(5)); // 10 console.log(triple(5)); // 15 console.log(double(10)); // 20 ``` Each call to `createMultiplier` produces a separate closure with its own `multiplier`. `double` and `triple` are independent. Calling one has no effect on the other. ### Intermediate: memoization ```javascript function memoize(fn) { const cache = {}; // Private cache, shared across all calls to the returned fn return function(...args) { const key = JSON.stringify(args); if (key in cache) { return cache[key]; // Cache hit } const result = fn(...args); cache[key] = result; return result; }; } const heavyCalc = memoize((n) => { let sum = 0; for (let i = 0; i < n; i++) sum += i; return sum; }); console.log(heavyCalc(1000000)); // computed console.log(heavyCalc(1000000)); // returned from cache ``` `cache` is a private variable that persists across every call to the returned function. No global variable, no class. The closure is the only thing keeping `cache` alive and hidden. ### Advanced: the loop trap and three ways to fix it ```javascript // Problem: var creates one shared binding for all event handlers const buttons = document.querySelectorAll('button'); for (var i = 0; i < buttons.length; i++) { buttons[i].addEventListener('click', function() { console.log(i); // Always logs buttons.length, not 0, 1, 2 }); } // Fix 1: let - most readable, works in all modern environments for (let i = 0; i < buttons.length; i++) { buttons[i].addEventListener('click', function() { console.log(i); // Each callback captures its own i }); } // Fix 2: IIFE - captures the value before the loop advances (pre-ES6) for (var i = 0; i < buttons.length; i++) { buttons[i].addEventListener('click', (function(index) { return function() { console.log(index); // index is a local copy of i at this iteration }; })(i)); } ``` The IIFE approach works because calling a function creates a new scope. At each iteration, `i` is passed as an argument and bound to `index` locally. The inner function closes over `index`, not `i`. With `let`, the engine handles this automatically. Same result, no extra syntax.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.