07 Jan 2025

I was recently talking with a senior developer who was asking me JavaScript questions to gauge my knowledge. Before that conversation, I felt confident. After it, I felt deflated. It became clear that I still had work to do.
My goal is to become comfortable with the areas where I fell short. To that end, I want to bring clarity to one of the most important and confusing JavaScript concepts: closures.
Closures are tricky, but they become easier to understand when broken down. A closure happens when a function can remember and access variables from its outer scope, even after that outer scope has finished executing.
Key takeaway: Closures enable functions to remember and access variables from their surrounding context, even after that context has finished executing.
Here’s a simple example to illustrate how closures work. Create an HTML file, link a script.js file, and paste the following code. Refer back to this block throughout the article.
function outerFunction(outerVariable) {
return function innerFunction(innerVariable) {
console.log(`Outer Variable: ${outerVariable}`);
console.log(`Inner Variable: ${innerVariable}`);
};
}
const closureFunc = outerFunction('outside');
closureFunc('inside');Explanation:
outerFunction takes a parameter outerVariable.
It returns innerFunction, which takes its own parameter innerVariable.
innerFunction logs both outerVariable and innerVariable.
Even after outerFunction finishes executing, innerFunction retains access to outerVariable because of the closure.

Let’s reference the code block above and walk through it step by step.
outerFunction is defined with one parameter: outerVariable.
Instead of executing innerFunction, outerFunction returns it. The returned function retains access to outerVariable.
outerVariable belongs to outerFunction, but remains accessible inside innerFunction due to the closure.
innerFunction logs both outerVariable and innerVariable, demonstrating how closures preserve access to outer scope variables.

const closureFunc = outerFunction('outside');
outerFunction is called with the argument 'outside'.
It returns innerFunction.
The returned function is stored in closureFunc.
closureFunc('inside');
closureFunc is invoked with the argument 'inside'.
innerFunction logs both values.
outerVariable remains accessible even though outerFunction has completed.
In short: closureFunc is the returned innerFunction. It remembers the argument passed to outerFunction and accepts a new argument for itself.

Why doesn’t closureFunc('inside') invoke outerFunction again?
closureFunc is just a reference to innerFunction.
outerFunction is no longer involved after returning innerFunction.
Data encapsulation — variables can remain private.
Function factories — create functions with preset behavior.
State management — especially useful in asynchronous code.
Closures are a foundational JavaScript concept. While they can be confusing at first, understanding them deepens your grasp of functions and scope. With practice, closures become a natural and powerful part of your JavaScript toolkit.