webroad.online
  1. 1Web
  2. 2HTML
  3. 3CSS
  4. 4JavaScript
  5. 5TypeScript
  6. 6Git
  7. 7Tooling
  8. 8React
  9. 9State management
  10. 10Next.js
  11. 11Forms
  12. 12Data and backend
  13. 13SEO
  14. 14Tailwind CSS
  15. 15Animations
  16. 16Testing
  17. 17Architecture
JavaScript · Lesson 12 of 17

Classes and prototypes

class, private fields, getters, extends and what's under the hood.

Updated

What a class is

A class is a template for creating objects of the same kind. It describes once what data (properties) and behavior (methods) they have; then you create any number of instances with new.

class Account {
  constructor(owner) {          // runs on new — initializes the object
    this.owner = owner
    this.balance = 0
  }

  deposit(amount) {             // a method — behavior shared by every account
    this.balance += amount
  }
}

const a = new Account('Ana')    // an instance
const b = new Account('John')   // another instance, with its own data
a.deposit(100)

An analogy: the class is the cookie cutter, the instances are the cookies.

The parts of a class — kinds of members

Member Syntax Belongs to When
public field count = 0 each instance data visible from outside
private field #balance = 0 each instance, inaccessible from outside internal, protected data
method deposit() {} shared by all instances behavior
getter / setter get total() {} / set total(v) {} the instance a computed value read like a property
static static from(json) {} the class, not the instance factories, constants, helpers
class Account {
  #balance = 0                      // private — acc.#balance is a SyntaxError
  static currency = 'USD'           // Account.currency

  constructor(owner) { this.owner = owner }

  deposit(amount) {
    if (amount <= 0) throw new Error('Invalid amount')
    this.#balance += amount
    return this                     // allows chaining: acc.deposit(1).deposit(2)
  }

  get balance() { return this.#balance }   // acc.balance — no parentheses

  static fromJSON({ owner }) { return new Account(owner) }
}

Inheritance

extends creates a class that takes over everything from another one and adds or changes something:

class Savings extends Account {
  constructor(owner, rate) {
    super(owner)                // calls the parent's constructor — required before this
    this.rate = rate
  }

  addInterest() { this.deposit(this.balance * this.rate) }
}

Prototypes — what's under the hood

Classes are "syntactic sugar" over prototypes. Every object has a hidden link to another object (its prototype). When you ask for a property that doesn't exist on the object, JS looks for it in the prototype, then in the prototype's prototype — the prototype chain.

a.deposit                                 // not on a, found on Account.prototype
Object.getPrototypeOf(a) === Account.prototype   // true
a instanceof Account                      // true — Account.prototype is in the chain

That's why methods are shared (a single deposit in memory for every account), and [].map works on any array (it lives on Array.prototype).

Classes vs functions + objects

Classes Functions + plain objects
Protected internal state #private a closure
Inheritance extends composition (you combine functions)
this yes, with its traps not needed
In modern React only error boundaries everything: components, hooks

When to use classes in the frontend

  • Yes: custom errors (class HttpError extends Error), an API client / SDK, data structures (a queue, a cache), integrating with class-based libraries.
  • No: React components (they're functions today), simple data (an object literal is enough).
  • Prefer composition (small pieces combined) over deep inheritance hierarchies — A extends B extends C extends D becomes hard to change.

Summary

  • A class = a template; new creates instances; constructor initializes.
  • #private fields, getters, static, extends + super.
  • Under the hood: prototypes. In React, almost always functions.

Official sources

Exercises

Was this page helpful?

One tap — no account needed.