I was recently in a situation where I was talking to a senior developer, and he was asking me questions about JavaScript to gauge my knowledge. Before this conversation, I felt confident in my JavaScript knowledge. After that conversation, I felt deflated. I realized I still had a lot of work to do. My goal is to become comfortable with the aspects of JavaScript where I fell short during that conversation. To that end, I want to bring clarity to the JavaScript concept of closures.
Understanding 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. This unique behavior allows inner functions to retain access to variables defined in an enclosing function.
Key takeaway: Closures enable functions to remember and access variables from their surrounding context, even after that context has finished executing.
Example of a Closure
Here’s a simple example to illustrate how closures work. Create an HTML file, link a script.js page and copy this code so you can see the console output.
Refer back to this block throughout the articleExplanation:outerFunction
takes a parameter outerVariable
- It returns
innerFunction
, which itself takes a parameter innerVariable
. -
innerFunction
logs both outerVariable
and innerVariable.
- Even after
outerFunction
has finished executing, innerFunction
retains access to outerVariable due to the closure.
Breaking Down the Code
Reference code block aboveStep 1: Defining the Outer Function
function outerFunction(outerVariable)
outerFunction
is defined with one parameter, outerVariable.
Step 2: Returning the Inner Function
return function innerFunction(innerVariable)
Instead of executing innerFunction
, outerFunction
returns it.
The returned innerFunction
retains access to outerVariable
.
Step 3: Parameters in Scope
outerVariable
belongs to outerFunction
.
Step 4: Logging Inside the Inner Function
console.log(`Outer Variable: ${outerVariable}`);
console.log(`Inner Variable: ${innerVariable}`);
innerFunction
logs both outerVariable
and innerVariable
, demonstrating how closures work.
An outer function defines a variable and returns an inner function. When innerFunction is called later, it still has access to outerVariable from the outer function's scope, even though the outer function has finished executing.How the Function Calls Work
1. Calling outerFunction
const closureFunc = outerFunction('outside');
What happens:outerFunction
is called with the argument 'outside'.- It logs 'outside'.
- It returns
innerFunction
, which gets stored in closureFunc
.
2. Calling innerFunction via closureFunc
closureFunc('inside');
What happens:closureFunc
is invoked with the argument 'inside'.- innerFunction logs:
Outer Variable: outside
Inner Variable: inside
- Even though
outerFunction
has completed, innerFunction
still has access to outerVariable
because of the closure.
closureFunc is the returned innerFunction(), which remembers the outerFunction argument (in this case the string 'outside') and takes a new argument for itself.. in this case ('inside')
- closureFunc is the returned innerFunction.
- It 'remembers' the outerFunction argument (via the closure).
- It also accepts a new argument specifically for innerFunction.
Common Misunderstandings
Why doesn’t closureFunc('inside') invoke outerFunction?
It might seem like calling closureFunc should somehow trigger outerFunction, but here’s the key point:
- closureFunc is just a reference to innerFunction.
- outerFunction is no longer involved after returning innerFunction.
Even though outerFunction has finished executing, its variables remain accessible to innerFunction because of the closure.
Why Closures Matter
Closures are powerful because they allow:
- Data encapsulation: Variables can be kept private within a function.
- Function factories: You can create functions with preset configurations.
- Maintaining state: Closures help manage state in asynchronous operations.
Final Thoughts
Closures are a fundamental concept in JavaScript. While they may seem confusing initially, understanding how they work will deepen your knowledge of JavaScript functions and scope. Closures are widely used in callbacks, function factories, and state management in asynchronous code.
With practice, closures will become a natural and powerful tool in your JavaScript toolkit.