Getters and setters inside a class body

A computed value usually starts as a method and becomes a property when someone forgets the parentheses. An accessor lets it be read as a property while still being computed.

class Cart {
  constructor(lines) { this.lines = lines; }

  get total() {
    return this.lines.reduce((n, l) => n + l.price, 0);
  }

  set total(v) {
    throw new Error('total is derived');
  }
}

new Cart(lines).total;   // no parentheses

The setter that throws is the useful half: without it, assigning to total silently shadows the getter and every later read returns the assigned value. Accessors are on the prototype like methods, so they are inherited, and they are invisible in JSON.stringify — a serialised cart will not contain its total unless something puts it there.