On JavaScript Closures (With Examples of Interview Questions)
by @markupninja@hachyderm.io on , tagged guest-posts, javascript, closures, variables, examples, interviewing (share on Mastodon or on Bluesky)
A JavaScript closure (WebGlossary.info, MDN) is a function that retains access to its lexical scope even when the function is executed outside of that scope. Closures allow functions to “remember” the environment in which they were created.
It is important to understand some of their concepts:
- Lexical scope: A function has access to variables from its own scope, its parent function’s scope, and the global scope.
- Function inside a function: Closures are created when an inner function is returned or used outside its parent function.
- Persistent state: Closures can “remember” variables even after the outer function has executed.
Closures sometimes come up in web developer interviews.
What are some questions that can be asked?
“What Is a Closure in JavaScript, and Why Is It Useful?”
A closure is a function that retains access to its lexical scope, even when executed outside of that scope. Closures allow functions to “remember” variables from their outer function, making them useful for data encapsulation, maintaining state, and function factories. For example, a counter function can retain a private count variable while allowing controlled access through an inner function. This makes closures essential for writing modular and reusable code in JavaScript.
“How Do Closures Help With Data Privacy? Provide an Example”
Closures help with data privacy by restricting access to variables, ensuring they cannot be modified from outside a function. This is particularly useful for encapsulation, similar to private variables in object-oriented programming.
function createCounter() {
let count = 0; // Private variable
return function () {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
Here, count
is not accessible directly, but the returned function can increment and return its value, maintaining controlled access.
Logging I: “What Will Be Logged in the Following Code, and Why?”
function outer() {
let count = 0;
return function () {
count++;
console.log(count);
};
}
const func1 = outer();
const func2 = outer();
func1(); // ?
func1(); // ?
func2(); // ?
The output will be:
1
2
1
Each call to outer()
creates a new closure with a separate count variable. func1
and func2
do not share state because they are separate instances. The first two calls to func1()
increment the first closure’s count, while func2()
starts a fresh counter at 1
. This demonstrates how closures create independent stateful functions.
Logging II: “What Will Be Logged in the Following Code, and in What Order?”
function makeRequest() {
const requests = [];
for (let i = 1; i <= 3; i++) {
requests.push(() => new Promise(resolve => {
setTimeout(() => {
resolve(i * 2);
}, 100 - i * 20);
}));
}
return requests;
}
const requests = makeRequest();
Promise.all(requests.map(req => req())).then(console.log);
The output will be:
[2, 4, 6]
What makes this interesting:
- Each promise factory closes over its own
i
value thanks tolet
- The timeouts are actually executed in reverse order (60ms, 40ms, 20ms)
- Despite different resolution times,
Promise.all
maintains the original array order - The closure captures the multiplication operation, not just the value
In a question like this, what’s being tested is understanding of:
- Closure interaction with promises
Promise.all
behavior with array orderingsetTimeout
closure capturing- Function factories with async operations
It’s a practical example showing how closures work with asynchronous code and promise composition.
“What Are Some Potential Pitfalls of Using Closures, and How Can They Be Avoided?”
There are at least three potential issues with closures:
Memory leaks: If closures hold references to large objects, they can prevent those objects from being garbage collected, leading to an increased use of memory and risk of leaks.
Unintended variable retention: Be mindful of the variables captured by closures. If not managed properly, closures can retain references to variables that are no longer needed (which can also lead to memory leaks). Release references when they are no longer necessary.
Unexpected behavior in loops: Using
var
in loops can lead to unexpected results becausevar
is function-scoped. It is best to uselet
orconst
within loops to ensure that each iteration captures the correct value, avoiding common pitfalls associated with closures.
—These are just a handful of many, many examples of how closures can be used in JavaScript, and what questions you could be asked. The key is to understand the concept and how it can be applied in different scenarios.
(Frontend Dogma accepts guest posts as long as they aren’t predominantly AI-generated or promotional. Although guest posts are being reviewed, Frontend Dogma cannot and does not vouch for their accuracy, and does not necessarily endorse recommendations made in them.)