Closure In JavaScript is a function that retains access to variables from its outer scope, even after the outer function has finished executing.
This definition may seem complicated at the first, but don’t worry in this article— we will Learn how JavaScript closures transform functions from stateless to stateful, with practical examples and the mental model that makes everything click.
in this article we will cover:
- Why We Need Closures
- What is Lexical Scope
- Understanding Closures and Lexical Scope
- Working with Closure
- Use Cases for Closures
- Final Remarks
Why We need closures
Closures are very important to know how today’s most popular frameworks and libraries work take an example of React Hooks they are most vital part of react part and essential when you have knowledge and skills of how closure work it will be easier to navigate around those.
other Advantages of working with closures are:
- Helps keep the function pure without mutating the external of it.
- Helps make variable private the scope they are declare in.
What is Lexical scope:
Lexical Scope by definition
Lexical scope in JavaScript are rules that determines how and where the variable can be accessed in your program.
Lexical Scope under the hood
lexical scope under the hood is an object in JavaScript’s scope either in a function or any JavaScript scope you may think of
that object which we call Lexical scope keeps track of:
- the variable in that scope.
- the value of
thisof that scope. - the pointer that points to the outer lexical scope if there any if there is no other scope above that the point is null
so, when we have a function that looks like this
let x = 10
function test(){
let y = 4
}
This function lexical scope would look like this:
as we demonstrated in this diagram each scope has its own lexical scope which points to the outer lexical scope above it.
when in the test() you try to access the variable x JavaScript will look for it in the lexical scope of test() if it is not there, it will then look in its outer lexical scope pointer which is the global one and if we have x there we will then access to x and use it inside our function of test()
What happens when we don’t have it, we will continue go up the tree and until we hit the lexical scope that’s points to null and that’s when you get the error saying: Uncaught ReferenceError: x is not defined
function test(){
let y = 4
return x
}
console.log(test()) //Uncaught ReferenceError: x is not defined
Understanding Closures and Lexical scope:
To understand closures, we first need to understand how the normal function works under the hood. this will helps knowing what options with closure that are limited when using usual function.
A normal function when its execution finishes there is a memory management mechanism in JavaScript that is known as garbage collection .
Garbage collection: is a mechanism for JavaScript to clean up the allocated memory for non-primitive data types when they are no longer in use, again if you don’t know how functions and non-primitive data types how are stored in memory, I have an article which talks about it which you can find here.
garbage collection removes the allocated memory when the function is no longer in use makes the functions after execution reset all its variables and values declared with in its block scope of a function or the passed argument to the function.
function test(){
let y = 4
return y+= 1
}
console.log(test()) // 5
console.log(test()) // 5
console.log(test()) // 5
console.log(test()) // 5
No matter how long we call the function the result will always be the same unless we have variable y in global scope
let y = 4;
function test(){
return y+= 1
}
console.log(test()) // 5
console.log(test()) // 6
console.log(test()) // 7
console.log(test()) // 8
What happens here is the garbage collection will clear out any allocated space that is no longer has reference with global object in case of browser the global object is the window object, When the function finished executing it cuts reference and ties to the window object which then makes it eligible for garbage collected and wipe out all of its declared variable and passed argument if there are any.
But when we have declared a variable in a global scope, the declared variable connection with the window object will stay forever until you remove the variable from the global scope, which is why the variable is updated to a new value every time we call the function, because the variable state is updating.
To give you a better mental model shift around these concepts you should think of them like:
- stateless function: a function that has no access to variable(s) outside of its scope the function variables are stateless
- stateful function: the function with access variable(s) from outside of its scope they have state which can be tracked and updated over time which makes them persists data and update as we go.
Working with closure:
You know how the whole life cycle of execution of functions and a knowledge of how lexical scope works.
I want you to take a look at this snippet:
function counter(){
let n = 0
return function(){
return n += 1
}
}
this is the most common example you will see online of working with closures, and yeah this is how we create a closure we return a function and wrap it inside another function so when we call the outer function in our case counter it will return another function which will look like this [Function (anonymous)] .
because as we have seen in the example above the counter returns a function, now the returned function has reference to its outer scope variable n thus making connection with its lexical scope, since we have the connection with variable outside of the scope of the inner function it will persist the state of data.
remember the stateful mental model** it states that “the function with access to variable(s) from outside of its scope they have state which can be tracked and updated over time which makes them persists data and update as we go.”
It means that since we have the reference to the variable n garbage collection will not remove variable n from the memory because there is some function that is referencing it,
The outer function will be removed but n will stay because of the connection it has with the inner function and that’s why mostly you will hear people define closure as “a function that retains its lexical scope even after its outer function has finished executing”
now because of closures we’ve made variable n from stateless to state preservation and now we can use the function to increment numbers we want, and here is how we execute the closure function
const count = counter() // assing the return function to a new variable
// use that variable to get the counts from the inner function
console.log(count()) //1
console.log(count()) //2
console.log(count()) //3
Tricky Question
Try and figure out what is a closure from the below 2 snippets
A
function outer(){
let p = 0;
return function(){
return p += 1
}
}
const results = outer()
console.log(results()) // 1
console.log(results()) // 2
console.log(results()) // 3
B
function outer(){
return function(){
let p = 0;
return p += 1
}
}
const results = outer()
console.log(results()) // 1
console.log(results()) // 1
console.log(results()) // 1
Well option A is the real answer because it has reference to its outer scope while B is returning another function true,
but we are declaring variable within that function which means the moment the function finishes executing garbage collection will wipe out everything from function B
remember the stateless mental model: it states that “a function that has no access to variable(s) outside of its scope the function variables are stateless”
Use Cases for Closures
Let’s try to explore one practical use cases for closures that feels more real and makes sense to understand closure.
Example for use Case:
suppose we are creating a function that lists the names of members of our app and the country they are representing; with the stateless way we can create our function like this:
function introduceYou(name, country){
return `Hi I am ${name} and I am representing ${country}`
}
console.log(introduceYou('Jabo', 'Rwanda') // Hi I am Jabo and I am respresenting Rwanda
console.log(introduceYou('Arnold', 'Rwanda') // Hi I am Arnold and I am respresenting Rwanda
console.log(introduceYou('Landry', 'Rwanda') // Hi I am Landry and I am respresenting Rwanda
console.log(introduceYou('John', 'Rwanda') // Hi I am John and I am respresenting Rwanda
console.log(introduceYou('Abdul', 'UAE') // Hi I am Abdul and I am respresenting UAE
console.log(introduceYou('Jane', 'USA') // Hi I am Jane and I am respresenting USA
console.log(introduceYou('Doe', 'USA') // Hi I am Doe and I am respresenting USA
Problems in Changing the existing structure
This approach works, but here’s a catch, you came up with the idea like you know what let instead use the city names instead of country names to change this would look very tedious say you have a thousand representing Rwanda, and millions representing USA.
Solution for the change
it would be hard to change all of those places and to be honest it is time consuming, but there is a solution , you can use closure to reference the thing that is common to the data we trying to capture in our case country some users may have the same country or many of them so we will make that a default reference that will stay in memory for as long as we wish to and this will make it easier changing it to something new like city
closure solution implementation
function introduceYou(locaction){
return function user(name){
return `Hi I am ${name} and I am representing ${location}`
}
}
//now we create a function that will handle people from different region
const rwandaUsers = introduceYou("Rwanda")
const americanUsers = IntroduceYou("USA")
const uaeUsers = IntroduceYou("UAE")
//and now we create a function for users based on the variable country we created
console.log(rwandaUsers("Jabo")) // Hi I am Jabo and I am respresenting Rwanda
console.log(rwandaUsers("Arnold")) // Hi I am Arnold and I am respresenting Rwanda
console.log(americanUsers("Jane")) // Hi I am Jane and I am respresenting USA
console.log(uaeUsers("Abdul")) // Hi I am Abdul and I am respresenting UAE
now if you want to change to their city all we need to be go where we’ve declared a certain user of a certain country and change the passed argument to be the city instead of country
example (let’s change rwandaUsers to use the city Kigali instead)
const rwandaUsers = introduceYou("Kigali")
console.log(rwandaUsers("Jabo")) // Hi I am Jabo and I am respresenting Kigali
console.log(rwandaUsers("Arnold")) // Hi I am Arnold and I am respresenting Kigali
Final Remarks
When I was preparing this article, I found that React states are built using closures which links back to that mental model shift for stateless and stateful function I shared with you, those mental model helped me realize why React state are called state and how they are able to persist data throughout component life cycle.
I hope this article gave you both understanding and confidence with closures. If you found it valuable, please share it with someone who's struggling with this concept—it might be the explanation that finally makes it click for them.

Top comments (0)