Private Fields
In short: a field name starting with # (#balance) is genuinely
private — it can only be read or written from inside the class
that declares it. Code outside the class can't reach it even by trying:
obj.#balance outside the class body isn't just blocked, it's a
SyntaxError, because # names aren't valid to even write outside a
class that declares them. This is stronger than the old convention of
naming a field _balance with a leading underscore — that's just a hint
to other programmers, easily ignored; #balance is enforced by the
language itself.

Read and update a balance through public methods only
The #balance field itself is never touched from outside — every
interaction goes through ordinary public methods that happen to read or
write it internally:
a.balance prints 150 — but that's the get balance() method
running, not direct access to #balance. From outside, #balance simply
doesn't exist as something you can name.
Confirm a private field is invisible everywhere
Unlike a regular property, a #field doesn't show up in any of the usual
ways of listing or serialising an object's properties:
Object.keys(a) is [], JSON.stringify(a) is "{}", and even
a["#balance"] — trying to sneak in through bracket notation with the
literal string "#balance" — gives undefined. There is no way to reach
a #field from outside its class, by any route.
The trap: assuming an underscore is the same thing
A leading underscore, this._balance, is only a convention — a
polite signal that says "please don't touch this directly." JavaScript
does nothing to stop anyone from ignoring it:
class OldStyleAccount {
constructor(balance) {
this._balance = balance; // convention only — NOT enforced
}
}
const a = new OldStyleAccount(100);
a._balance = 999999; // works, silently, from anywhereNothing prevents that assignment — _balance is a completely ordinary,
public property that merely looks private by agreement. #balance closes
that gap entirely: there's no syntax that lets outside code reach it, on
purpose or by accident.
Quick practice
What happens when code outside the class tries to write a.#balance?
Does Object.keys(accountInstance) include private #fields?
How does this._balance (underscore) differ from this.#balance (private field)?
Given a public get balance() method that internally reads this.#balance, what does calling a.balance from outside actually do?
What does a["#balance"] return, trying to access a private field through bracket notation from outside?
🎯 Your turn
Given
class Account { #balance; constructor(balance) { this.#balance = balance; } deposit(amount) { this.#balance += amount; } get balance() { return this.#balance; } },
explain why a.balance works from outside the class while a.#balance
does not, and why this is a stronger guarantee than naming a regular field
_balance by convention.
Write your explanation and submit it — there's no code to run here, just your reasoning.
