Notifications

No notifications

JavaScript's class keyword (ES2015) is sweeter syntax over its prototype-based inheritance — there are no real classes under the hood, just objects linking to other objects. This page covers the modern class syntax (constructor, methods, getters/setters, static, private #fields, extends / super) and a peek at the prototype chain that powers it all.

On this page

Detailed Theory

# Classes & Prototypes

Class basics

class User {
    constructor(name, age) {
        this.name = name;
        this.age  = age;
    }

greet() { return hi, I'm ${this.name}; } }

const u = new User("Alice", 25); u.greet(); // "hi, I'm Alice" u instanceof User; // true

  • new creates a fresh object, sets up the prototype chain, runs constructor, then returns the object.
  • this inside class methods refers to the instance.

Getters & setters

class Temp {
    constructor(c) { this._c = c; }
    get f() { return this._c * 9 / 5 + 32; }
    set f(v) { this._c = (v - 32) * 5 / 9; }
}
const t = new Temp(100);
t.f;            // 212  (no parens — looks like a property)
t.f = 32;       // 0

Static methods & properties

Belong to the class itself, not instances.
class MathUtil {
    static PI = 3.14159;
    static area(r) { return MathUtil.PI * r * r; }
}
MathUtil.area(5);    // 78.54

Private fields — #name

True hard-private (not just convention). Only readable/writable inside the class body.
class Account {
    #balance = 0;            // private
    deposit(n) { this.#balance += n; }
    get balance() { return this.#balance; }
}
const a = new Account();
a.deposit(100);
a.balance;           // 100
a.#balance;          // ❌ SyntaxError

Inheritance with extends and super

class Animal {
    constructor(name) { this.name = name; }
    speak() { return ${this.name} makes a sound; }
}

class Dog extends Animal { constructor(name, breed) { super(name); // ← MUST call super() before using this this.breed = breed; } speak() { return ${super.speak()} (woof); // call parent method } }

const d = new Dog("Rex", "Lab"); d.speak(); // "Rex makes a sound (woof)" d instanceof Dog; // true d instanceof Animal; // true

The prototype chain (under the hood)

A class is just sugar for:
function User(name) { this.name = name; }
User.prototype.greet = function () { return hi ${this.name}; };
const u = new User("Alice");
  • Every object has an internal link [[Prototype]] (accessible via Object.getPrototypeOf(obj)).
  • Property lookup: first own property, then prototype, then prototype's prototype, … up to Object.prototypenull.
  • That's how u.greet() finds greet even though it's not on u itself.
Object.getPrototypeOf(u) === User.prototype;     // true
Object.getPrototypeOf(User.prototype) === Object.prototype;  // true

Class methods aren't auto-bound

const fn = u.greet;
fn();    // ❌ TypeError — this is undefined in strict mode

// fixes: const fn1 = u.greet.bind(u); const fn2 = () => u.greet();

React class components used to need this.handleClick = this.handleClick.bind(this) in the constructor — class fields with arrow functions sidestep this:
class Btn {
    handleClick = () => { console.log(this); };   // bound to instance
}

When to actually use classes

  • Things with identity + behavior + state: User, Cart, Connection, custom Errors.
  • Library APIs where extends/instance checks matter.
  • Otherwise, plain objects + functions are usually enough — JS isn't Java.