JavaScript Mastery: From Fundamentals to Modern ES2024+
HomeInsightsCoursesJavaScriptPrivate Methods & Accessors
OOP Architecture

Private Methods & Accessors

Master true encapsulation in JavaScript. Learn how to use the `#` prefix to create private class members that are physically inaccessible from outside the class instance, ensuring robust API boundaries.

Hard Encapsulation with #

For decades, JavaScript developers used the "underscore convention" (e.g., `_privateVar`) to signal that a property shouldn't be touched. However, this was only a social contract—the property was still fully accessible. **Class Private Fields** introduce "Hard Encapsulation" at the engine level. Once a field is prefixed with `#`, attempting to access it from outside the class scope results in a **SyntaxError**, not just `undefined`.

JAVASCRIPT
// Engineering Pattern: Hard Encapsulation
class SecureVault {
    // Private Field Declaration
    #masterKey; 
    #balance = 0;

    constructor(initialKey) {
        this.#masterKey = initialKey;
    }

    // Private Method for Internal Logic
    #logTransaction(type, amount) {
        console.log(`Vault event: ${type} of $${amount}`);
    }

    deposit(amount, key) {
        if (key !== this.#masterKey) throw new Error("Unauthorized");
        this.#balance += amount;
        this.#logTransaction("DEPOSIT", amount);
    }

    get balance() {
        return this.#balance; // Read-only view
    }
}

const vault = new SecureVault("SECRET_123");
vault.deposit(100, "SECRET_123");
// console.log(vault.#balance); // SyntaxError: Private field '#balance' must be declared in an enclosing class

Static Private & Initialization

Privacy extends to class-level members as well. **Static Private Fields** are useful for managing shared internal state, such as singleton instances or connection pools, without exposing them to global modification. Combined with **Static Initialization Blocks**, you can perform complex, one-time setup logic for these private members.

JAVASCRIPT
// Technical Insight: Static Private Members
class SingletonService {
    static #instance;

    static {
        // Static initialization block
        this.#instance = new SingletonService();
    }

    static getInstance() {
        return this.#instance;
    }

    #internalState = "init";
    
    // Private internal method
    #refresh() { /* ... */ }
}

Technical Insight: The "Why" of #

Why did TC39 choose the `#` symbol? Unlike `private` in TypeScript (which is purely compile-time), JavaScript needed a way to distinguish private properties from public ones at **runtime** without breaking backward compatibility or complicating property lookup performance. The `#` prefix ensures that the Engine knows immediately if a property access is valid or should throw a error, maintaining O(1) look-up performance.

Private Member Checklist:

  • ✅ **Stability:** Use `#` for internal state that consumers should never rely on.
  • ✅ **Security:** Hide sensitive data like keys or internal IDs from `JSON.stringify()`.
  • ✅ **Cleanliness:** Use private methods to break down complex public functions into manageable units.
  • ✅ **Inheritance:** Remember that private fields are NOT visible to subclasses (use composition if needed).
  • ✅ **Evolution:** Move from underscore conventions (`_prop`) to true `#` fields for modern libraries.

What's Next?

You've mastered Modern JS! Now let's wrap up this batch and review our progress in the walkthrough.