A closure is a function that remembers the variables available in the place where it was created, even after that surrounding code has finished running. This simple behavior appears in many programming languages, including JavaScript, Python, Ruby, Swift, and Kotlin. Once understood, closures make callbacks, event handlers, private state, and functional programming patterns much easier to reason about.
Consider a function that creates a counter. The outer function defines a number and returns another function that increases it:
function createCounter() { let count = 0;
return function () { count += 1; return count; }; }
const counter = createCounter();
console.log(counter()); // 1 console.log(counter()); // 2
At first glance, count might seem to disappear when createCounter finishes. It does not. The returned function keeps a reference to count, so the variable remains available as long as the returned function can still be used. That combination of a function and the surrounding variables it remembers is a closure.
This is particularly useful when code needs to preserve state without exposing it directly. In the counter example, other parts of the program cannot change count by assigning to it. They can only interact with it through the function. This creates a small, practical form of data protection without requiring a class or a large object structure.
Closures also appear whenever a function is passed to another function. A common example is a delayed action:
function greetLater(name) { setTimeout(function () { console.log("Hello, " + name); }, 1000); }
greetLater("Maya");
The callback runs later, after greetLater has returned, but it still knows the value of name. This pattern is used constantly in web development. Buttons, network requests, timers, animations, and file operations often depend on callbacks that remember information from the moment they were created.
One detail causes frequent bugs: closures remember variables, not always the value those variables had at one particular moment. This matters in loops. If several callbacks share the same changing variable, they may all read its final value when they eventually run. Modern JavaScript code often avoids this problem with let, which creates a new binding for each loop iteration. Another option is to create a separate function scope for every callback.
Closures are not automatically good or bad. They become difficult when they capture more data than necessary or live longer than expected. In a browser application, a closure connected to an event listener may keep references to large objects, DOM elements, or application data. If the listener is never removed, that memory may remain in use after the related screen has disappeared. Clear ownership and cleanup are therefore important.
Readable naming helps as well. A closure that quietly captures five unrelated variables can be harder to maintain than a small object with explicit fields. The best use of closures usually has a clear purpose: keeping a piece of state private, preparing a callback with context, or building a reusable function from a configuration value.
A useful way to recognize a closure is to ask two questions: does a function use a variable from an outer scope, and can that function run after the outer scope has finished? If both answers are yes, a closure is probably involved. Understanding that relationship turns a concept that can seem abstract into something visible in everyday code.