You’ve probably used a closure today. In an event handler, a timer, or a callback that reaches for a variable outside itself. The name can sound intimidating; the idea is surprisingly familiar.
Let’s build a clear mental model, walk through a small example, and turn it into an answer you can actually use in an interview.
Start with the surrounding scope
Imagine a function that prepares a greeting for one person. It returns another function, ready to be called later:
function createGreeting(name) {
const greeting = `Hello, ${name}!`;
return function sayHello() {
return greeting;
};
}
const greetMaya = createGreeting('Maya');
console.log(greetMaya()); // "Hello, Maya!"When we call greetMaya(), createGreeting has already finished. Yet sayHello can still read greeting. It was defined inside that scope, and it retains access to it.
This is lexical scope: where a function is written determines the surrounding variables it can access. Moving the function into another variable or calling it elsewhere doesn’t change that relationship.
A function and its environment
Think of the returned function and its surrounding environment as a pair. The environment supplies the variables that the function needs when it runs.
This is not a special feature of returned functions. Functions form closures when they are created. Returning one simply makes the lifetime of that access easier to see.
Remembering a variable, not a snapshot
Here’s where interview questions become interesting. A closure keeps access to a binding. It doesn’t automatically freeze the value that binding held when the function was created.
function createCounter() {
let count = 0;
return function increment() {
count += 1;
return count;
};
}
const next = createCounter();
console.log(next()); // 1
console.log(next()); // 2
console.log(next()); // 3Every call to next() reaches the same count binding. The updated value is there the next time we call it. Calling createCounter() again creates a separate environment with its own counter.
What does this print?
Take a moment before opening the answer. There are two calls to the factory, but three calls to the returned functions.
const first = createCounter();
const second = createCounter();
console.log(first());
console.log(first());
console.log(second());Think it throughWhat are the three outputs, and why?
The output is:
1
2
1
first and second were created by different calls to createCounter. Each call created its own count binding. Calling first twice updates its counter to 2; second still begins at 0, so its first call returns 1.
A useful follow-up: if we instead wrote const second = first, both variables would refer to the same function and share the same counter.
The loop question
A familiar variation creates callbacks inside a loop. The important question is whether those callbacks share one changing binding or get a binding for each iteration.
Inside a for loop |
|
|
|---|---|---|
Binding | One shared binding | A new binding per iteration |
Callbacks run after |
|
|
Why? | Each callback reads the same final | Each callback reads its iteration’s |
The timing matters here. The comparison assumes the callbacks run after the loop finishes, as they do with a timer. It doesn’t describe a callback invoked immediately during each iteration.
Explain it in an interview
Start with the concept, show a small example, and explain which variable the function can still access. You don’t need to begin with a long definition.
For a follow-up, talk about a real use: retaining configuration for an event handler, keeping internal state behind a small API, or explaining why a callback sees a particular variable.
One last detail: closures don’t automatically cause memory leaks. Retained references can keep data reachable, so release callbacks or subscriptions when you no longer need them.
Keep this with you
-
Look at where the function was created to find its surrounding scope.
-
Ask which binding it reads, and whether that binding changes.
-
Distinguish multiple calls to a factory from multiple calls to one returned function.
For a deeper reference, read the MDN guide to closures.
Thanks for reading this.