Skip to main content

Closures

A closure is a function that "remembers" the variables from the scope where it was created, even after that scope has finished executing. In other words, a closure is a function together with the lexical environment in which it was born.

Theory

TL;DR

  • A closure appears when a function is declared inside another function and uses that function's variables.
  • The outer function may finish, but the captured variables are not destroyed while something still references them.
  • A closure keeps a reference to the variable, not a copy of it, so state survives between calls.
  • Every call to the outer function creates a new, independent environment with its own variables.
  • It is the main way to build private data, counters, function factories and memoization.
  • The classic trap: var in a loop together with setTimeout gives one shared variable to every iteration.

Quick example

javascript
function outer() { let count = 0; // a variable of the outer function function inner() { count++; console.log(count); } return inner; } const counter = outer(); // we call the outer function counter(); // 1 counter(); // 2 counter(); // 3

What happens:

  1. outer() runs and creates the variable count and the inner function inner.
  2. It returns inner, which remembers where it was created.
  3. Even after outer() finishes, the variable count is not destroyed, because inner still references it.

The chain in short:

text
outer() -> created count -> inner() -> remembered count

A closure is a function plus its environment

Every closure stores a lexical environment (scope), that is, the set of variables it has access to.

javascript
function makeAdder(x) { return function (y) { return x + y; // access to "x" from the outer scope }; } const add5 = makeAdder(5); console.log(add5(10)); // 15 console.log(add5(7)); // 12

Explanation:

  • The function makeAdder creates and returns a new function.
  • The returned function closes over the value of x (in our case 5).
  • Even after makeAdder finishes, x stays alive.

What matters is that makeAdder(5) and makeAdder(10) produce two different functions with two different environments that know nothing about each other.

Private variables and state

Closures are the standard way to make data unreachable from outside.

javascript
function createCounter() { let count = 0; return { increment() { count++; }, decrement() { count--; }, get() { return count; } }; } const counter = createCounter(); counter.increment(); counter.increment(); console.log(counter.get()); // 2

Here count is not reachable directly from outside, but it is reachable from the closed-over functions (increment, decrement, get). This is how you create private variables in JavaScript.

A closure keeps a reference, not a value

A function remembers not the value but a reference to the variable in the outer environment.

javascript
function make() { let value = 1; return () => console.log(value++); } const f = make(); f(); // 1 f(); // 2, the variable "value" is alive between calls

That is exactly why the counter works: both calls read and update the same memory slot.

The classic loop mistake

javascript
for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 1000); } // one second later: 3, 3, 3

Why:

  • All the functions closed over one and the same variable i, declared with var in the function scope.
  • By the time the timer fires, the loop has finished and i is already 3.

The fix is let, which creates a fresh variable on every iteration:

javascript
for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 1000); } // 0, 1, 2

Summary and analogy

What it isExample
A function that "remembers" variables from the place where it was createdreturn function () { console.log(a); }
The variables live on even after the outer function has finishedYes
It creates private data and stateYes
Widely used in callbacks, event handlers and modulesYes

A closure is like a backpack in which a function carries along all the variables it needs from the place where it was born.

Common mistakes

  • Thinking a closure copies the value. It holds a reference to the variable, so a later change to that variable is visible inside.
  • Using var in a loop with an asynchronous callback. You get the final counter value on every iteration. Use let, or wrap the body in a function.
  • Assuming every call to the outer function shares one state. The opposite is true: each call creates a separate environment, so two counters are independent.
  • Creating closures in loops over thousands of items for no reason. Each one keeps its environment in memory, and if the reference is never released the data is not collected by the garbage collector, which is a memory leak.
  • Confusing closures with this. A closure captures variables, not this: in a regular function this is determined by how it is called, and only an arrow function takes this from the surrounding scope.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.