Sometimes you want something that looks like a property but is actually calculated. For that, classes have getters, written with the keyword get:
class Person {
constructor(firstName, lastName, birthYear) {
this.firstName = firstName;
this.lastName = lastName;
this.birthYear = birthYear;
}
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
get age() {
return 2026 - this.birthYear;
}
}
const person = new Person("Emma", "Garcia", 2001);
console.log(person.fullName);
console.log(person.age);
person.firstName = "Chloe";
console.log(person.fullName);
It prints
Emma Garcia 25 Chloe Garcia
Notice there are no parentheses: you write person.fullName, not person.fullName(). It's used just like a normal property, but it's worked out fresh every time you read it. So when the first name changes, fullName automatically updates.
A setter, written with set, runs when you assign to a property. It's perfect for validation: checking that a value makes sense before accepting it.
class Thermostat {
constructor() {
this._temperature = 22;
}
get temperature() {
return this._temperature;
}
set temperature(value) {
if (value < 10 || value > 30) {
console.log(`❌ ${value}°C is out of range! Keeping ${this._temperature}°C.`);
return;
}
this._temperature = value;
console.log(`🌡️ Temperature set to ${value}°C`);
}
}
const thermostat = new Thermostat();
thermostat.temperature = 25;
thermostat.temperature = 99;
console.log(thermostat.temperature);
It prints
🌡️ Temperature set to 25°C ❌ 99°C is out of range! Keeping 25°C. 25
(The underscore in _temperature is a common convention meaning "this is an internal property; please don't touch it directly." But it's only a polite request. Anyone can still change it. For real privacy, keep reading.)