makeCounter is the canonical closure interview question. If you understand why the returned function remembers its variables, you understand closures. The question starts simple and escalates — here's how to handle both versions.
Related deep-dive: JS Foundations #3 — Closures & Lexical Scope
What is makeCounter()?
makeCounter() is the simplest possible demonstration of closures — the mechanism by which an inner function retains access to variables from its enclosing scope, even after that scope has finished executing. Understanding this question means understanding closures, period.
When you call makeCounter(), it creates a local variable (typically count) and returns a function. That returned function "closes over" count — meaning it holds a live reference to the variable, not a frozen copy. Each subsequent call mutates the same count, and no outside code can touch it. This is the module pattern in microcosm: private state exposed through a public API.
The interview usually escalates from the simple one-function version to an object with multiple methods (increment, decrement, reset, getValue), all sharing the same closed-over state. The key realization: every call to makeCounter() spawns an independent closure with its own private count. Two counters never interfere.
Closures power virtually every modern JS pattern — event handlers, memoization, currying, debouncing, and the entire React hooks model. If you can explain makeCounter, you can explain all of them.
The Problem
Version I:
"Write a makeCounter function that returns a counter. Each call to the returned function increments and returns the count, starting from 0."
Version II (the interviewer will extend):
"Now modify it so the counter object has four methods: increment(), decrement(), reset(), and getValue()."
Thought Process
A closure is created when a function "remembers" the variables from its outer scope, even after that outer scope has finished executing. For a counter, the outer function makeCounter creates a variable. The inner function closes over it. Each call to makeCounter creates a new, independent closure.
Key insight: the variable is private. Nothing outside the returned function can access or modify it directly. This is the module pattern in its simplest form.
Step 1 — Version I: Simple Closure Counter
Loading editor...
That's it. One variable, one returned function that closes over it. But the interviewer isn't done.
Step 2 — Version II: Counter Object with Methods
The interviewer now says: "Turn it into an object with four methods."
Loading editor...
Step 3 — Why Closures Make This Work
Here's what the interviewer wants you to articulate:
- When
makeCounteris called, a new execution context is created with a local variablecount. - The returned object's methods hold references to
count— not a copy, the actual variable. - Even after
makeCounterreturns and its execution context is popped off the call stack,countis not garbage-collected because the closures still reference it. - Each call to
makeCountercreates a new, separatecountvariable — the closures are independent.
This is the same mechanism that powers the module pattern, memoization, and event handlers in loops.
Step 4 — Edge Cases
Starting from a non-zero value: The initialValue parameter handles this. Default it to 0 so the counter works with no arguments.
Calling reset(): Should return to initialValue, not 0 (unless initialValue was 0). This is a detail interviewers notice — if you reset to 0 instead of the initial value, that's a bug.
What if someone passes a non-number? The native behavior would still work — JavaScript would coerce it. Don't add validation unless asked.
Making count truly unreachable: Since we return an object with methods, those methods can access count. But there's no way to get count directly — only through getValue(). The data is truly private.
Full Solution
Loading editor...
What Interviewers Are Testing
- Closure fundamentals — you understand that inner functions retain access to outer variables after the outer function returns
- Lexical scope — you know
countis scoped tomakeCounter, not the returned function - Data privacy — you recognize that closures create truly private state (no way to access
countexcept through the API) - Independent instances — you understand that each
makeCounter()call creates a fresh closure with its owncount
Complexity
| Operation | Time | Space |
|---|---|---|
| All methods | O(1) | O(1) |
Interview Tips
- Say "closure" immediately — when the interviewer asks "how would you implement a counter?", respond "this is a closure — an inner function that captures a variable from its enclosing scope." This shows you recognize the pattern.
- Write version I in 3 lines — don't over-explain the simple version. Save your energy for version II, where the discussion happens.
- Use
++countnotcount++— pre-increment returns the updated value directly, so you can writereturn ++countinstead ofcount++; return count;. It's a small thing that shows fluency. - Mention garbage collection — when asked "how does the variable stay alive?", explain that the closure reference prevents GC. This signals deeper knowledge.