Classes, Prototypes & this
Class Syntax
class Course {
#students = 0; // private field — invisible outside
constructor(title, price) {
this.title = title;
this.price = price;
}
enroll() {
this.#students++;
}
get revenue() { // getter — read like a property
return this.#students * this.price;
}
static fromJSON(json) { // called on the class, not instances
const data = JSON.parse(json);
return new Course(data.title, data.price);
}
}
const js = new Course("JS Mastery", 199);
js.enroll();
js.revenue; // 199 — no parentheses
js.#students; // SyntaxError — truly private
Course.fromJSON('{"title":"CSS","price":99}');Inheritance
class VideoCourse extends Course {
constructor(title, price, hours) {
super(title, price); // must call before using this
this.hours = hours;
}
get revenue() {
return super.revenue * 1.1; // call the parent's version
}
}Under the Hood: Prototypes
class is syntax over JavaScript’s real model: prototype chains. Methods live once on Course.prototype; instances link to it. When you call js.enroll(), JS looks on the instance, then up the chain until it finds the method. That’s also why "abc".toUpperCase() works — strings link to String.prototype. You rarely touch prototypes directly, but knowing the model demystifies the language.
this — the Part Everyone Trips On
this is determined by how a function is called, not where it’s written:
const course = {
title: "JS",
describe() {
return this.title; // called as course.describe() → this = course
},
};
course.describe(); // 'JS'
const fn = course.describe;
fn(); // undefined — called bare, this is lost!The classic bug — passing a method as a callback:
button.addEventListener("click", course.describe); // this is lost
// Fix 1: arrow wrapper (most common)
button.addEventListener("click", () => course.describe());
// Fix 2: bind
button.addEventListener("click", course.describe.bind(course));Arrow functions don’t have their own this — they inherit it from where they’re defined. That’s why class field arrows are handy for callbacks:
class Counter {
count = 0;
increment = () => { // arrow field: this is always the instance
this.count++;
};
}When to Use Classes
JS is multi-paradigm. Use classes for stateful things with identity (a player, a connection, a game). For data transformation, plain functions + objects are often simpler — don’t force OOP where a function will do.