Private Variables with Closures
Hide state behind a function scope.
Private Variables with Closures is a free JavaScript Academy lesson on CoddyKit. This is lesson 2 of 4. Any 3 lessons of this learning path are free to read in full — after that, CoddyKit PRO unlocks every lesson, plus hands-on practice with a built-in code editor and a 24/7 AI tutor. It is part of the JavaScript Academy learning path, and your progress syncs across the web and the CoddyKit app. The JavaScript Academy course includes 4 lessons in total.
Privacy Through Closures
JavaScript long lacked a built-in way to make object fields private. Closures provide one: variables inside a function are invisible outside, yet accessible to functions defined within.
function secretBox() {
let secret = "hidden";
return {
reveal: () => secret
};
}
const box = secretBox();
console.log(box.reveal()); // "hidden"
console.log(box.secret); // undefinedA Counter With Private State
The canonical example: a counter whose count cannot be tampered with directly. Only the returned methods can change it.
function makeCounter() {
let count = 0;
return {
increment: () => ++count,
value: () => count
};
}
const c = makeCounter();
c.increment();
c.increment();
console.log(c.value()); // 2The State Is Untouchable
There is no way to reach count from outside. Attempts to read it return undefined; you must go through the methods.
function makeCounter() {
let count = 0;
return { inc: () => ++count, get: () => count };
}
const c = makeCounter();
c.count = 100; // creates an unrelated property
console.log(c.get()); // 0 - real count untouchedEnforcing Rules
Because access goes through methods, you can enforce invariants, such as never letting a balance go negative.
function account(start) {
let balance = start;
return {
withdraw: (n) => {
if (n > balance) return "insufficient";
balance -= n;
return balance;
},
balance: () => balance
};
}
const a = account(100);
console.log(a.withdraw(150)); // "insufficient"
console.log(a.withdraw(40)); // 60Multiple Private Fields
A closure can guard several variables at once. They are all hidden, shared only among the returned methods.
function user(name) {
let logins = 0;
let lastSeen = null;
return {
login: () => { logins++; lastSeen = "now"; },
stats: () => ({ name, logins, lastSeen })
};
}
const u = user("Ada");
u.login();
console.log(u.stats()); // { name: "Ada", logins: 1, lastSeen: "now" }Private Helper Functions
Not just data; you can hide internal functions too. Only the methods you expose are public.
function temperature() {
let celsius = 0;
const toF = (c) => c * 9 / 5 + 32; // private
return {
set: (c) => { celsius = c; },
fahrenheit: () => toF(celsius)
};
}
const t = temperature();
t.set(100);
console.log(t.fahrenheit()); // 212Independent Instances
Each call to the factory creates separate private state, so instances never interfere with one another.
function makeCounter() {
let n = 0;
return { inc: () => ++n };
}
const a = makeCounter();
const b = makeCounter();
console.log(a.inc(), a.inc()); // 1 2
console.log(b.inc()); // 1Read-Only Exposure
Expose a getter without a setter to make a value read-only from the outside while still mutable internally.
function timer() {
let ticks = 0;
setupInternally();
function setupInternally() { ticks = 3; }
return { ticks: () => ticks };
}
const t = timer();
console.log(t.ticks()); // 3 - no way to set it outsideComparison: Class Fields
Modern JavaScript also supports #private class fields. Closures remain useful for factory functions and when you want privacy without classes.
class Counter {
#count = 0;
inc() { return ++this.#count; }
}
const c = new Counter();
console.log(c.inc()); // 1Why It Works
The returned methods are closures over the outer variables. The outer function has returned, but its scope lives on because those closures still reference it.
A Memoization Cache
Private state is great for caches. The cache object stays hidden, persisting between calls.
function memoSquare() {
const cache = {};
return (n) => {
if (cache[n] === undefined) cache[n] = n * n;
return cache[n];
};
}
const sq = memoSquare();
console.log(sq(4)); // 16
console.log(sq(4)); // 16 (cached)Quick Check
Private variables with closures.
Recap
Closures create privacy: variables inside a factory are unreachable from outside but shared among the returned methods. This enables tamper-proof counters, balances with invariants, hidden helpers, and caches. Each factory call yields independent state. Modern #private class fields are an alternative, but closures shine for factory-style code.
Learn JavaScript with an AI tutor — free
Write and run real code in your browser, get instant help from a 24/7 AI tutor, and pick up where you left off on the web or in the app.
- Courses
- 59
- Lessons
- 200
Frequently Asked Questions
Is the “Private Variables with Closures” lesson free?
Yes — any 3 lessons of the JavaScript Academy learning path, including “Private Variables with Closures”, are free to read in full here on the web. After that, CoddyKit PRO unlocks every lesson, plus interactive practice with a built-in code editor and a 24/7 AI tutor. The JavaScript Academy course includes 4 lessons in total.
What will I learn in “Private Variables with Closures”?
Hide state behind a function scope. You practise JavaScript Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start JavaScript Academy?
No prior experience is required. JavaScript Academy on CoddyKit is structured for beginners through advanced learners, so you can start here or from the beginning and move at your own pace. This is lesson 2 of 4.
How long does the “Private Variables with Closures” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this JavaScript Academy lesson?
Yes. Every JavaScript Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- How Closures Capture State
- Private Variables with Closures
- The Module Pattern (IIFE)
- Factory Functions