Classes

JavaScript · Practical · 15 min

Classes are the standard way to model stateful objects and are used throughout the DOM and Node APIs.

A class bundles a constructor and methods for creating objects of the same shape:

class Account {
  #balance = 0;                 // private field
  static count = 0;             // shared by the class
  constructor(owner) { this.owner = owner; Account.count++; }
  deposit(amount) { this.#balance += amount; return this; }
  get balance() { return this.#balance; }
}

The pieces:

  • constructor runs on new Account("Ada") and initialises the instance.
  • Methods are shared by all instances.
  • Fields (name = value;) are created per instance before the constructor body runs.
  • Private members start with # and are inaccessible outside the class body — truly private, not a naming convention. #field in obj checks whether an object has one.
  • Getters/setters (get balance()) look like properties to callers.
  • `static` members belong to the class itself: Account.count, factory methods like Account.fromJSON(). A static { } block runs once for class setup.

Inheritance uses extends. A subclass constructor must call super(...) before it touches this; methods can call super.method() to extend the parent's behaviour. instanceof checks the chain: savings instanceof Account.

Classes are mostly syntax over prototypes (covered later), with differences: class bodies are always strict, classes can't be called without new, and class declarations are in the TDZ until defined (not hoisted like functions).

Prefer composition over deep hierarchies: one level of extends is common, three is a smell. And not everything needs a class — plain objects and functions are often simpler.

Example

class Account {
  #balance = 0;
  static count = 0;

  constructor(owner) {
    this.owner = owner;
    Account.count++;
  }
  deposit(amount) {
    if (amount <= 0) throw new RangeError("amount must be positive");
    this.#balance += amount;
    return this;                       // allow chaining
  }
  get balance() { return this.#balance; }
}

class Savings extends Account {
  constructor(owner, rate) {
    super(owner);                      // must come before using this
    this.rate = rate;
  }
  addInterest() { return this.deposit(this.balance * this.rate); }
}

const s = new Savings("Ada", 0.05).deposit(100);
s.addInterest();
console.log(s.balance, s instanceof Account, Account.count); // 105 true 1
// s.#balance -> SyntaxError outside the class

Remember

  • Fields and #private members are declared in the class body
  • extends + super() for inheritance; call super before using this
  • static members live on the class; getters make computed properties

Watch out for

  • Forgetting super() in a subclass constructor (ReferenceError when this is used).

Check yourself

  1. In a subclass constructor, what happens if you assign this.x = 1 before calling super()?
  2. What is true of a field declared as #token in a class?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes