Methods, Getters & Setters
Give your classes behaviour — plus properties that compute themselves and guard their own values.
A method is a function that lives on an object — you've called hundreds of them (push, toUpperCase, toFixed…). In a class, any function you write in the body becomes a method available on every instance.
Inside a method, this follows the rule you learned in Fundamentals: this is whatever is before the dot when the method is called.
class Player {
constructor(name) {
this.name = name;
this.score = 0;
}
addPoints(points) {
this.score += points;
}
describe() {
return `${this.name} has ${this.score} points`;
}
}
const ana = new Player("Ana");
const ben = new Player("Ben");
ana.addPoints(30);
console.log(ana.describe()); // Ana has 30 points
console.log(ben.describe()); // Ben has 0 pointsSame method, two instances — this points at ana in the first call and ben in the second. Methods can also call each other through this: this.addPoints(5) works inside any other method.
Now something new. Look at this awkward pattern:
class Rectangle {
constructor(width, height) {
this.width = width;
this.height = height;
this.area = width * height; // stored once… and instantly stale
}
}
const r = new Rectangle(3, 4);
r.width = 10;
console.log(r.area); // still 12 — wrong!Storing a derived value is a trap: change the inputs and the stored copy is out of date.
The fix is a getter — a method that pretends to be a property. You define it with the get keyword and read it without parentheses. It recomputes every time it's read:
class Rectangle {
constructor(width, height) {
this.width = width;
this.height = height;
}
get area() {
return this.width * this.height;
}
}
const r = new Rectangle(3, 4);
console.log(r.area); // 12
r.width = 10;
console.log(r.area); // 40 — always fresh- Why you read a getter without
()— the value's already been returned
Getters recompute on every read. What does this log?
class Playlist {
constructor() {
this.songs = [];
}
get count() {
return this.songs.length;
}
}
const p = new Playlist();
console.log(p.count);
p.songs.push("Song A");
p.songs.push("Song B");
console.log(p.count);- A0 2
- B0 0
- C2 2
- Dundefined 2
A setter is the mirror image: a method that runs when a property is assigned. Define it with the set keyword — perfect for validation:
class Product {
constructor(price) {
this._price = price;
}
get price() {
return this._price;
}
set price(value) {
if (value < 0) {
console.warn("Price can't be negative — ignored");
return;
}
this._price = value;
}
}
const p = new Product(20);
p.price = 35; // setter runs → accepted
p.price = -5; // setter runs → rejected with a warning
console.log(p.price); // 35Note the underscore in _price. The getter/setter pair is named price, so the real storage needs a different name — and a leading underscore is the long-standing convention for "internal, please don't touch directly."
The underscore is only a polite request, though — nothing stops outside code from writing p._price = -99. Two lessons from now you'll meet private fields, which turn that request into a locked door.
Complete the getter so order.total (no parentheses) returns the computed sum:
class Order {
constructor(price, qty) {
this.price = price;
this.qty = qty;
}
___ total() {
return this.price * this.qty;
}
}- Functions in a class body are methods; inside them,
thisis the instance the method was called on. - A getter (
get name() {...}) computes a value on every read and is accessed without parentheses — ideal for derived data that must never go stale. - A setter (
set name(value) {...}) runs on every assignment — ideal for validation. _underscorenames signal "internal" by convention; real enforcement arrives with private fields in lesson 4.
Next: what happens when two classes share most of their behaviour — inheritance.