You've probably reached for a let count = 0 at module scope to hand out incrementing IDs — for DOM elements, log lines, or React keys before you had a real id source. The catch is that a module-level variable is shared by everyone; if two parts of your app want their own counter, they collide. A factory fixes that: each call hands out a fresh, independent counter.
Implement makeCounter(start). It takes an integer start and returns a new function. Each call to that returned function yields the next value in the sequence, starting at start. Two counters made by two separate makeCounter calls must not share state. This is a closure exercise — see MDN on closures if the term is unfamiliar.
function makeCounter(start) {
// returns a function () => number
// 1st call -> start
// 2nd call -> start + 1
// 3rd call -> start + 2
// ...
}
const next = makeCounter(5);
next(); // 5
next(); // 6
next(); // 7
const a = makeCounter(0);
const b = makeCounter(100);
a(); // 0
a(); // 1
b(); // 100
a(); // 2 <- a's state is untouched by b
start itself, not start + 1. The next value is computed for the next call.makeCounter(...) call must give back a function with its own private state. They can never share or overwrite each other.makeCounter() is called with no argument, the counter starts at 0.step arguments. Always +1 per call.window, module.exports, or any shared object.You'll write a factory function that hands out a fresh, private counter every time it's called — the canonical "closure in action" exercise.
You want a tiny function that gives you 5, then 6, then 7 on three successive calls. You could keep a let count = 5 near the top of your file, but then every caller across your codebase is sharing that one variable. The factory version, makeCounter(5), hands back a function that has its own private count. Two makeCounter(0) calls produce two completely independent counters — what one does has no effect on the other.
That privacy is what a closure buys you: a function can "remember" variables from where it was created, even after that surrounding function has returned.
Think of makeCounter as a little factory that, on each visit, walks into a fresh room, writes start on a sticky note labelled count, and hands you a remote control. Pressing the remote tells the function in that room: "read the sticky note, hand me that number, then add one to the sticky note." Visit the factory again and you get a brand-new room with its own sticky note. The room never disappears as long as you keep the remote.
It's tempting to declare the counter variable at the top of the module — that feels simpler than nesting it inside a function.
// naive — count lives outside makeCounter, so every counter shares it
let count;
function makeCounter(start) {
count = start;
return function () {
const value = count;
count = count + 1;
return value;
};
}
This passes the first test you'd write — makeCounter(5) does return 5, 6, 7 — but it fails the moment you make two counters. Calling makeCounter(100) reassigns the same count to 100, which trashes whatever progress the first counter had made. The state needs to live somewhere only one counter can see it.
Move the state inside makeCounter. Because let count is declared in the function body, each call to makeCounter creates a fresh count. The returned function closes over that specific count — and only that one.
function makeCounter(start = 0) {
// `count` is declared on every call to makeCounter, so each returned
// function gets its own private variable — that's the whole point.
let count = start;
return function next() {
// "Return then increment": snapshot the current value first,
// bump count for the next call, then return what we snapshotted.
const value = count;
count = count + 1;
return value;
};
}
module.exports = { makeCounter };
Four things to notice. First, start = 0 is a default parameter so makeCounter() (no argument) starts at 0. Second, let count = start is inside the function — that's what makes each counter private; a var or let at module scope would be shared. Third, we save count into value before incrementing, because the spec says "return then increment" — the very first call must return start, not start + 1. Fourth, the returned function is named next (just for stack traces); naming it changes nothing about behavior.
JavaScript's count++ operator executes both the return and the increment in a single expression: it evaluates to the current value, then bumps the variable. You might also see this written with a post-increment expression as return count++;. That's exactly equivalent to the three-line version above — count++ evaluates to the current count and then bumps it — but spelling it out as three lines makes the "return then increment" shape impossible to misread.
Take const next = makeCounter(5) and call next() three times.
When makeCounter(5) runs, it creates a new scope with count = 5 and returns the inner next function (which now has that scope baked in). Now press the remote:
next() #1 — read count (it's 5), save it to value, set count = 6, return value which is 5.next() #2 — read count (now 6), save it, set count = 7, return 6.next() #3 — read count (now 7), save it, set count = 8, return 7.Now spin up a second counter: const other = makeCounter(0). That call creates a brand-new scope with its own count = 0. The first counter's count is still sitting at 8. They never touch each other.
count = count + 1; return count; makes the first call return start + 1 instead of start. With makeCounter(5), you'd get 6, 7, 8 when the spec asks for 5, 6, 7. Always snapshot the value before bumping (or use count++, which does both in the right order).count outside makeCounter — if let count lives at module scope, every counter shares it. const a = makeCounter(0); const b = makeCounter(100); a(); would return 100 (b's start overwrote a's), not 0. Keep count inside the function body so each call gets its own.start directly — writing start = start + 1; return start; works in isolation but is confusing: start is meant to be the constant initial value, not the running counter. Use a separate count variable so the names match the roles.function makeCounter(start) (no default) means makeCounter() sets count = undefined, and the first call returns undefined followed by NaN (because undefined + 1 is NaN). The start = 0 default prevents that.const for count — const count = start; count = count + 1; throws TypeError: Assignment to constant variable. because const bindings can't be reassigned. The internal counter must be a let.Keep practising the same patterns with a nearby challenge.
No submissions yet
Share your approach and start the discussion.
You've probably reached for a let count = 0 at module scope to hand out incrementing IDs — for DOM elements, log lines, or React keys before you had a real id source. The catch is that a module-level variable is shared by everyone; if two parts of your app want their own counter, they collide. A factory fixes that: each call hands out a fresh, independent counter.
Implement makeCounter(start). It takes an integer start and returns a new function. Each call to that returned function yields the next value in the sequence, starting at start. Two counters made by two separate makeCounter calls must not share state. This is a closure exercise — see MDN on closures if the term is unfamiliar.
function makeCounter(start) {
// returns a function () => number
// 1st call -> start
// 2nd call -> start + 1
// 3rd call -> start + 2
// ...
}
const next = makeCounter(5);
next(); // 5
next(); // 6
next(); // 7
const a = makeCounter(0);
const b = makeCounter(100);
a(); // 0
a(); // 1
b(); // 100
a(); // 2 <- a's state is untouched by b
start itself, not start + 1. The next value is computed for the next call.makeCounter(...) call must give back a function with its own private state. They can never share or overwrite each other.makeCounter() is called with no argument, the counter starts at 0.step arguments. Always +1 per call.window, module.exports, or any shared object.You'll write a factory function that hands out a fresh, private counter every time it's called — the canonical "closure in action" exercise.
You want a tiny function that gives you 5, then 6, then 7 on three successive calls. You could keep a let count = 5 near the top of your file, but then every caller across your codebase is sharing that one variable. The factory version, makeCounter(5), hands back a function that has its own private count. Two makeCounter(0) calls produce two completely independent counters — what one does has no effect on the other.
That privacy is what a closure buys you: a function can "remember" variables from where it was created, even after that surrounding function has returned.
Think of makeCounter as a little factory that, on each visit, walks into a fresh room, writes start on a sticky note labelled count, and hands you a remote control. Pressing the remote tells the function in that room: "read the sticky note, hand me that number, then add one to the sticky note." Visit the factory again and you get a brand-new room with its own sticky note. The room never disappears as long as you keep the remote.
It's tempting to declare the counter variable at the top of the module — that feels simpler than nesting it inside a function.
// naive — count lives outside makeCounter, so every counter shares it
let count;
function makeCounter(start) {
count = start;
return function () {
const value = count;
count = count + 1;
return value;
};
}
This passes the first test you'd write — makeCounter(5) does return 5, 6, 7 — but it fails the moment you make two counters. Calling makeCounter(100) reassigns the same count to 100, which trashes whatever progress the first counter had made. The state needs to live somewhere only one counter can see it.
Move the state inside makeCounter. Because let count is declared in the function body, each call to makeCounter creates a fresh count. The returned function closes over that specific count — and only that one.
function makeCounter(start = 0) {
// `count` is declared on every call to makeCounter, so each returned
// function gets its own private variable — that's the whole point.
let count = start;
return function next() {
// "Return then increment": snapshot the current value first,
// bump count for the next call, then return what we snapshotted.
const value = count;
count = count + 1;
return value;
};
}
module.exports = { makeCounter };
Four things to notice. First, start = 0 is a default parameter so makeCounter() (no argument) starts at 0. Second, let count = start is inside the function — that's what makes each counter private; a var or let at module scope would be shared. Third, we save count into value before incrementing, because the spec says "return then increment" — the very first call must return start, not start + 1. Fourth, the returned function is named next (just for stack traces); naming it changes nothing about behavior.
JavaScript's count++ operator executes both the return and the increment in a single expression: it evaluates to the current value, then bumps the variable. You might also see this written with a post-increment expression as return count++;. That's exactly equivalent to the three-line version above — count++ evaluates to the current count and then bumps it — but spelling it out as three lines makes the "return then increment" shape impossible to misread.
Take const next = makeCounter(5) and call next() three times.
When makeCounter(5) runs, it creates a new scope with count = 5 and returns the inner next function (which now has that scope baked in). Now press the remote:
next() #1 — read count (it's 5), save it to value, set count = 6, return value which is 5.next() #2 — read count (now 6), save it, set count = 7, return 6.next() #3 — read count (now 7), save it, set count = 8, return 7.Now spin up a second counter: const other = makeCounter(0). That call creates a brand-new scope with its own count = 0. The first counter's count is still sitting at 8. They never touch each other.
count = count + 1; return count; makes the first call return start + 1 instead of start. With makeCounter(5), you'd get 6, 7, 8 when the spec asks for 5, 6, 7. Always snapshot the value before bumping (or use count++, which does both in the right order).count outside makeCounter — if let count lives at module scope, every counter shares it. const a = makeCounter(0); const b = makeCounter(100); a(); would return 100 (b's start overwrote a's), not 0. Keep count inside the function body so each call gets its own.start directly — writing start = start + 1; return start; works in isolation but is confusing: start is meant to be the constant initial value, not the running counter. Use a separate count variable so the names match the roles.function makeCounter(start) (no default) means makeCounter() sets count = undefined, and the first call returns undefined followed by NaN (because undefined + 1 is NaN). The start = 0 default prevents that.const for count — const count = start; count = count + 1; throws TypeError: Assignment to constant variable. because const bindings can't be reassigned. The internal counter must be a let.Keep practising the same patterns with a nearby challenge.
No submissions yet
Share your approach and start the discussion.