Encapsulation
Hide an object's internals behind a clean interface using private #fields and getters/setters, so state can't be corrupted from outside.
Encapsulation means bundling data with the methods that operate on it, and hiding the internal details. Callers use a small, safe interface; they can't reach in and put the object into a broken state. Modern JS supports true privacy with # fields.
class BankAccount {
#balance = 0; // truly private — # marks it
deposit(amount) {
if (amount <= 0) throw new Error('must be positive');
this.#balance += amount;
}
get balance() { // controlled read access
return this.#balance;
}
}
const acc = new BankAccount();
acc.deposit(100);
console.log(acc.balance); // 100
// console.log(acc.#balance) // SyntaxError — private outside the class#balance can only be touched by code inside the class. Outsiders go through deposit() (which validates) and the balance getter (read-only). There's no way to set a negative balance from outside.
Before # fields: the _underscore convention
Older code marks 'private' fields with a leading underscore (this._balance) — but that's only a convention; nothing stops access. The # prefix is real, enforced privacy. Closures are another classic way to hide state (a variable captured in a factory function, as in the Closures lesson).
What happens if you try to read `acc.#balance` from outside the class?
Key takeaways
- Encapsulation hides internals behind a small, safe public interface.
- `#field` declares a truly private field — inaccessible outside the class.
- Expose controlled access with methods and get/set, validating on the way in.
- The old `_name` underscore is convention only; closures are another way to keep state private.
What does this print?
class Counter {
#n = 0;
bump() { this.#n++; }
get value() { return this.#n; }
}
const c = new Counter();
c.bump(); c.bump(); c.bump();
console.log(c.value);Reading `acc.#balance` outside the class is a SyntaxError. What's the right way to expose it?
class Acc { #balance = 100; }
console.log(new Acc().#balance);Complete the declaration of a truly private field.
class Vault { secret = 42; }
Give a Counter class a private #count starting at 0, an increment() method, and a read-only value getter. Calling increment twice then reading value should give 2.
Try it live — edit the code and hit Run to see the output: