Use class syntax for OOP patterns — constructors, instance methods, static members, private fields, getters/setters, and inheritance with extends and super.
Why: classes are syntactic sugar over prototypal inheritance. They make OOP patterns cleaner with constructor, methods, static members, private fields, and extends.
class Animal {
#name; // private field
constructor(name, sound) {
this.#name = name;
this.sound = sound;
}
speak() {
return `${this.#name} says ${this.sound}`;
}
get name() { return this.#name; }
static create(name, sound) {
return new Animal(name, sound);
}
}
class Dog extends Animal {
constructor(name) {
super(name, 'woof'); // call parent constructor
}
fetch(item) {
return `${this.name} fetches the ${item}!`;
}
}
const dog = new Dog('Rex');
console.log(dog.speak()); // 'Rex says woof'
console.log(dog.fetch('ball')); // 'Rex fetches the ball!'
console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // true
const cat = Animal.create('Whiskers', 'meow');
console.log(cat.speak());Why: getters and setters let you run logic on property access/assignment. Static members belong to the class itself, not instances — useful for factories and utilities.
class Temperature {
#celsius;
constructor(celsius) {
this.#celsius = celsius;
}
get fahrenheit() {
return this.#celsius * 9/5 + 32;
}
set fahrenheit(f) {
this.#celsius = (f - 32) * 5/9;
}
get celsius() { return this.#celsius; }
set celsius(c) { this.#celsius = c; }
static fromFahrenheit(f) {
return new Temperature((f - 32) * 5/9);
}
toString() {
return `${this.#celsius.toFixed(1)}°C / ${this.fahrenheit.toFixed(1)}°F`;
}
}
const t = new Temperature(100);
console.log(t.toString()); // '100.0°C / 212.0°F'
console.log(t.fahrenheit); // 212
t.fahrenheit = 32;
console.log(t.celsius); // 0
const bodyTemp = Temperature.fromFahrenheit(98.6);
console.log(bodyTemp.toString()); // '37.0°C / 98.6°F'