The JavaScript Interview Question That Terrifies Beginners
If you spend enough time learning JavaScript, sooner or later someone will ask:
"Can you explain closures?"
For many beginners, this question feels scary.
Not because closures are impossible to understand.
But because most explanations make them sound more complicated than they really are.
You'll often hear definitions like:
"A closure is a combination of a function bundled together with references to its surrounding state."
Technically correct?
Yes.
Helpful for a beginner?
Not really.
When I first read that definition, I understood every English word individually.
But I still had no idea what a closure actually was.
So let's forget technical definitions for a moment.
Let's start with a simple banking story.
Because once you understand the story, closures become surprisingly easy.
The ATM Card Story
Imagine you open a bank account.
The bank creates:
Account Number
Balance
Customer Information
Suppose your account balance is:
₹50,000
Now you leave the bank.
Hours later, you visit an ATM.
Something interesting happens.
Even though you left the bank long ago, the ATM still knows:
Your account
Your balance
Your details
The ATM remembers information that was created earlier.
That's the key idea behind closures.
A closure allows a function to remember variables from its parent scope even after the parent function has finished executing.
Read that sentence again.
That's the entire concept.
Everything else is simply understanding how JavaScript makes it happen.
The Problem Closures Solve
Let's imagine JavaScript had no closures.
Consider:
function createUser() {
let username = "Aqad";
}
When the function finishes:
username disappears
This is what we learned in the Scope chapter.
Local variables belong to their function.
After execution completes, those variables should normally be gone.
But sometimes we want a function to remember information.
For example:
User sessions
Counters
Private variables
Shopping carts
Authentication data
Closures make this possible.
Your First Closure Example
Let's look at a simple example.
function outer() {
let name = "Aqad";
function inner() {
console.log(name);
}
return inner;
}
const result = outer();
result();
Output:
Aqad
At first glance, this looks normal.
But something unusual happened.
What Makes This Strange?
Let's analyze carefully.
When:
outer();
finishes executing,
normally: name
should disappear.
Because it belongs to:
outer()
Right?
But later:
result();
still prints: Aqad
How?
Where did JavaScript get that value from?
The answer is: Closure
The inner function remembered its parent scope.
Visualizing What Happens
Step 1: outer();
creates: name = Aqad
Step 2: JavaScript creates:
inner()
Step 3: The inner function is returned.
Step 4: Even though:
outer()
has finished,
JavaScript keeps:
name = Aqad
alive because the returned function still needs it.
This preserved connection is called a closure.
Think About the ATM Again
Bank:
Creates Account
Creates Balance
Stores Information
You leave.
Later:
ATM accesses account details.
Why?
Because the ATM maintains a connection to the account.
Similarly: inner()
maintains access to variables from:
outer()
even after execution finishes.
A More Practical Example
Imagine we want to count button clicks.
Without closures:
let count = 0;
function increment() {
count++;
console.log(count);
}
Works.
But:
count
is global.
Any code can change it.
That isn't ideal.
Let's use a closure.
Counter Using Closure
function createCounter() {
let count = 0;
return function() {
count++;
console.log(count);
};
}
const counter = createCounter();
counter();
counter();
counter();
Output:
1
2
3
Why This Is Amazing
Notice:
createCounter()
runs only once.
Yet:
count
continues updating.
The variable survives because of the closure.
Without closures:
count would reset every time
But because the returned function remembers its environment:
count stays alive
This is one of the most powerful features in JavaScript.
A Real ATM Example in Code
Let's build a simple bank account.
function bankAccount() {
let balance = 5000;
return function() {
console.log(balance);
};
}
const checkBalance = bankAccount();
checkBalance();
Output:
5000
The balance remains accessible.
Even though:
bankAccount()
finished long ago.
Depositing Money
Let's improve it.
function bankAccount() {
let balance = 5000;
return function(amount) {
balance += amount;
console.log(balance);
};
}
const deposit = bankAccount();
deposit(1000);
deposit(2000);
deposit(500);
Output:
6000
8000
8500
The function remembers the balance.
Exactly like an ATM remembering your account information.
Why Developers Love Closures
Closures allow us to create private data.
Consider:
function createUser() {
let password = "secret123";
}
Normally:
Anyone inside the function can access:
password
But we may not want outside code to modify it.
Closures help protect private information.
Creating Private Variables
Example:
function createAccount() {
let balance = 10000;
return {
showBalance() {
console.log(balance);
}
};
}
const account = createAccount();
account.showBalance();
Output:
10000
But:
console.log(balance);
Output:
ReferenceError
The variable remains private.
Only approved methods can access it.
Real World Example: Shopping Cart
Imagine an e-commerce website.
Customer adds products.
We need to remember cart information.
Closure example:
function createCart() {
let items = 0;
return function() {
items++;
console.log(
"Items in cart:",
items
);
};
}
const addToCart = createCart();
addToCart();
addToCart();
addToCart();
Output:
Items in cart: 1
Items in cart: 2
Items in cart: 3
The cart remembers its state.
This pattern appears everywhere in modern applications.
Closures and Event Handlers
Whenever a user clicks:
Add to Cart
Login
Buy Now
Submit
closures are often involved behind the scenes.
Many UI frameworks such as React depend heavily on closure behavior.
Even if developers don't realize it.
How JavaScript Creates a Closure
Let's revisit:
function outer() {
let name = "Aqad";
function inner() {
console.log(name);
}
return inner;
}
JavaScript notices:
inner()
depends on:
name
Therefore:
JavaScript preserves that variable.
Even after:
outer()
completes.
The relationship remains alive.
This preserved relationship is the closure.
A Simpler Definition
After seeing several examples, we can finally define closures properly.
A closure is created when:
A function remembers variables
from its parent scope
even after the parent function
has finished execution.
That's it.
No complicated wording required.
Common Beginner Mistake
Many beginners think:
Closure = Function Inside Function
Not exactly.
A nested function alone is not necessarily a closure.
A closure happens when:
The inner function
continues accessing
the outer function's variables.
That memory connection is the important part.
Another Interview Example
What is the output?
function test() {
let count = 0;
return function() {
count++;
console.log(count);
};
}
const increment = test();
increment();
increment();
increment();
Output:
1
2
3
Why?
Because the closure preserves:
count
between executions.
Why Closures Matter in Real Applications
Closures are used in:
Authentication systems
User sessions
Shopping carts
React hooks
Event listeners
API request handlers
Data privacy patterns
Caching systems
Timers and intervals
Even if you never intentionally create a closure, you'll use them constantly as a JavaScript developer.
The Memory Side of Closures
Closures are powerful.
But developers must use them carefully.
Remember:
JavaScript keeps referenced variables alive.
If large objects remain referenced unnecessarily:
Memory usage increases.
This can create memory leaks in large applications.
Most beginners don't face this problem.
But experienced developers keep it in mind.
Why Senior Developers Care About Closures
Closures are one of the clearest signs that a developer understands JavaScript deeply.
Because closures connect several concepts we've already learned:
Execution Context
Scope
Lexical Environment
Scope Chain
Function Execution
Closures are where all those ideas come together.
Once closures click, many advanced JavaScript topics suddenly become easier.
Summary we learned:
What closures are
Why closures exist
ATM and bank account analogy
Remembering variables after execution
Counter examples
Private variables
Shopping cart example
Event handler usage
Real-world applications
Common interview questions
Think of a closure like an ATM card.
You leave the bank.
The bank session ends.
But the ATM still remembers your account.
Similarly:
A function may finish execution.
But another function can still remember and access its variables.
That memory is the closure.
And now that we understand how JavaScript remembers information, it's time to understand how JavaScript handles something even more mysterious:
Tasks that take time.
API requests.
Timers.
User clicks.
Background operations.
To understand those, we need to learn one of the most famous topics in JavaScript.
Event Loop Explained Through a Restaurant and Waiter Analogy
At some point in every developer's journey, a strange question appears.
You learn that JavaScript is:
Single Threaded
Then you hear people say:
JavaScript handles thousands of users.
JavaScript handles API requests.
JavaScript handles timers.
JavaScript handles button clicks.
JavaScript handles file uploads.
And naturally you ask:
"Wait a minute..."
"If JavaScript can only do one thing at a time, how is all of this possible?"
It's a very reasonable question.
In fact, when I first learned JavaScript, this concept confused me more than closures.
Because on the surface, the two statements seem to contradict each other.
JavaScript does one thing at a time.
and
JavaScript handles many things simultaneously.
Both statements are true.
The secret lies in understanding:
The Call Stack
Web APIs
Callback Queue
Event Loop
Before we dive into technical terms, let's start with something much easier to imagine.
A restaurant.
The Restaurant Story
Imagine you walk into a restaurant.
There is:
One waiter
One kitchen
Many customers
The waiter receives orders.
Customer 1 says:
I want a burger.
Customer 2 says:
I want a pizza.
Customer 3 says:
I want pasta.
Now imagine the waiter behaves like this:
Take burger order
Go to kitchen
Stand there 20 minutes
Wait for burger
Return
Take next order
The restaurant would be terrible.
Customers would become frustrated.
Business would fail.
Good restaurants don't work this way.
Instead:
Take order
Send order to kitchen
Move to next customer
The waiter remains available.
The kitchen works in the background.
This is almost exactly how JavaScript works.
JavaScript Is The Waiter
Think of JavaScript as the waiter.
The Call Stack is the waiter's current task list.
The browser or Node.js environment acts like the kitchen.
Whenever JavaScript encounters a task that takes time:
API Request
Timer
Database Query
File Reading
User Click
JavaScript delegates that task.
Then continues serving other work.
This delegation mechanism is the foundation of asynchronous programming.
First Example
Consider:
console.log("Start");
setTimeout(() => {
console.log("Timer Finished");
}, 2000);
console.log("End");
Many beginners expect:
Start
(wait 2 seconds)
Timer Finished
End
But the actual output is:
Start
End
Timer Finished
Why?
Because JavaScript doesn't wait.
Just like a good waiter doesn't stand beside the oven waiting for food.
What Actually Happens?
Let's break it down.
Step 1
JavaScript executes:
console.log("Start");
Output:
Start
Step 2
JavaScript reaches:
setTimeout(...)
Instead of handling the timer itself:
It gives the task to the browser.
Think:
Waiter → Kitchen
The timer starts running elsewhere.
JavaScript immediately continues.
Step 3
JavaScript executes:
console.log("End");
Output:
End
Step 4
After 2 seconds:
The browser says:
Timer complete.
Now the callback becomes ready.
Eventually:
console.log("Timer Finished");
executes.
Output:
Timer Finished
Understanding the Components
To understand the Event Loop properly, we need to know four parts.
Call Stack
Web APIs
Callback Queue
Event Loop
Let's examine each one.
Part 1: Call Stack
We learned this in the previous chapter.
The Call Stack handles:
Function Calls
Execution Contexts
Current Tasks
Think of it as the waiter's hands.
The waiter can hold only one active task at a time.
Part 2: Web APIs
Web APIs are provided by the browser.
Examples:
setTimeout()
fetch()
addEventListener()
Important:
These are NOT part of JavaScript itself.
The browser provides them.
Think of Web APIs as:
Kitchen Staff
The waiter delegates work to them.
Part 3: Callback Queue
When a background task finishes:
Its callback does not immediately execute.
Instead:
It enters a waiting area.
This waiting area is called:
Callback Queue
Restaurant analogy:
Finished Orders Shelf
Food is ready.
But the waiter hasn't picked it up yet.
Part 4: Event Loop
Now we meet the star of the chapter.
The Event Loop.
Its job is surprisingly simple.
It constantly checks:
Is the Call Stack empty?
If:
YES
Then:
Move callback from queue
to Call Stack
That's it.
That's the Event Loop's entire responsibility.
Visualizing Everything Together
Imagine:
console.log("Start");
setTimeout(() => {
console.log("Timer");
}, 1000);
console.log("End");
Flow:
Start
↓
Call Stack
↓
setTimeout
↓
Browser API
↓
Timer Starts
↓
End
↓
Call Stack Empty
↓
Timer Finished
↓
Callback Queue
↓
Event Loop
↓
Call Stack
↓
Timer
Output:
Start
End
Timer
Why JavaScript Doesn't Freeze
Imagine JavaScript handled timers directly.
Example:
setTimeout(() => {
console.log("Done");
}, 10000);
If JavaScript waited 10 seconds:
The page would freeze.
Buttons wouldn't work.
Scrolling wouldn't work.
Everything would stop.
Instead:
JavaScript delegates the task.
The application remains responsive.
This is why modern web applications feel smooth.
Real World Example: Food Delivery App
Imagine a customer opens a food delivery app.
The app requests restaurant data.
fetch("/restaurants");
Fetching data may take:
500 ms
2 seconds
5 seconds
JavaScript doesn't wait.
Instead:
Send Request
Continue Running
The user can:
Scroll
Search
Click buttons
While the request is being processed.
When the response arrives:
The callback executes.
This creates a much better user experience.
Understanding User Click Events
Consider:
button.addEventListener("click", () => {
console.log("Button Clicked");
});
What happens?
JavaScript registers the event.
Then continues running.
It doesn't sit there waiting for a click.
That would be wasteful.
Instead:
The browser monitors clicks.
When a click occurs:
The callback enters the queue.
The Event Loop eventually executes it.
Why Event Loop Is So Important
Without the Event Loop:
Modern web applications would be impossible.
Think about:
WhatsApp Web
Gmail
YouTube
Facebook
Amazon
AQAD Marketplace
All these applications perform:
Network Requests
Notifications
Real-Time Updates
User Interactions
Background Tasks
The Event Loop coordinates all of it.
The Famous Interview Question
What is the output?
console.log("A");
setTimeout(() => {
console.log("B");
}, 0);
console.log("C");
Many beginners answer:
A
B
C
Actual output:
A
C
B
Why?
Because:
Even a zero-millisecond timer goes through:
Web API
↓
Queue
↓
Event Loop
↓
Call Stack
The callback still waits its turn.
Another Example
console.log("Start");
setTimeout(() => {
console.log("Timer");
}, 0);
console.log("Middle");
console.log("End");
Output:
Start
Middle
End
Timer
The timer callback executes only after the Call Stack becomes empty.
AQAD Marketplace Example
Imagine a retailer searches for products.
searchProducts();
The application sends a request:
fetch("/products");
JavaScript continues running.
Meanwhile:
Server Searches Products
Database Executes Query
Results Generated
When data arrives:
Callback Queue
↓
Event Loop
↓
Call Stack
Products appear on screen.
All without freezing the application.
Why Beginners Struggle With The Event Loop
Because the code appears sequential.
Example:
console.log("1");
setTimeout(() => {
console.log("2");
}, 1000);
console.log("3");
Many people read:
1
Wait
2
3
But JavaScript actually does:
1
3
2
The Event Loop changes the execution order.
Understanding this is the key to understanding asynchronous JavaScript.
Event Loop in One Sentence
If you remember only one thing from this chapter, remember this:
The Event Loop continuously checks whether the Call Stack is empty and moves completed callbacks into execution when JavaScript is ready.
Everything else is simply supporting detail.
Common Beginner Mistakes
Mistake 1
Thinking JavaScript waits for timers.
It doesn't.
Mistake 2
Thinking setTimeout(0) executes immediately.
It doesn't.
It still enters the queue.
Mistake 3
Thinking Web APIs belong to JavaScript.
They don't.
The browser or Node.js environment provides them.
Mistake 4
Thinking callbacks execute the moment they're ready.
They don't.
They wait until the Call Stack becomes empty.
Why Senior Developers Care About The Event Loop
The Event Loop explains:
Async behavior
Timers
API requests
User interactions
Performance issues
UI responsiveness
Many difficult JavaScript bugs become easier once you understand how the Event Loop works.
In fact:
Promises.
Async/Await.
Fetch.
Node.js.
React.
Almost every modern JavaScript technology relies on Event Loop behavior.
Summary we learned:
Why JavaScript appears asynchronous
The Restaurant and Waiter analogy
Call Stack
Web APIs
Callback Queue
Event Loop
Timer execution
User events
API requests
Common interview questions
Think of JavaScript as a smart waiter.
A bad waiter waits beside the oven.
A smart waiter delegates work and keeps serving customers.
JavaScript does the same thing.
It delegates long-running tasks, keeps working, and uses the Event Loop to know when completed tasks are ready.
And now that we understand how JavaScript handles asynchronous operations, it's time to learn one of the most important tools built on top of that system.
A tool that solved a major problem in JavaScript development.
A tool called:

0 Comments