JavaScriptClass Methods

Class Methods

A class has two kinds of methods. Instance methods live on the prototype and operate on a specific object via this. Static methods live on the class itself and behave more like namespaced helpers. The syntax is almost identical — one little keyword separates them — but their roles in a design are quite different.

The method shorthand

Inside a class body, methods are written without the function keyword. This is the same method shorthand you can use inside object literals.

JS
class Rectangle {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }

  // Instance method — lives on Rectangle.prototype
  area() {
    return this.width * this.height;
  }

  perimeter() {
    return 2 * (this.width + this.height);
  }
}

const r = new Rectangle(3, 4);
console.log(r.area());        // 12
console.log(r.perimeter());   // 14
Where methods actually live

Instance methods are not copied onto each object. They live on the class's prototype, and instances inherit them.

JS
const r = new Rectangle(3, 4);

console.log(Object.hasOwn(r, "area"));                          // false — not own
console.log(Object.hasOwn(Rectangle.prototype, "area"));        // true
console.log(r.area === Rectangle.prototype.area);               // true

Because the function is shared, you save memory: a thousand rectangles share one area function. It also means you can monkey-patch a method on the prototype and every instance picks it up immediately — useful for testing, hazardous in production.

Static methods — a preview

Prefixing a method with static attaches it to the class itself, not the prototype. Static methods are utility functions that belong to the class but do not operate on a specific instance.

JS
class Rectangle {
  constructor(w, h) { this.width = w; this.height = h; }

  area() { return this.width * this.height; }

  static fromSquare(side) {
    return new Rectangle(side, side);
  }
}

const sq = Rectangle.fromSquare(5);
console.log(sq.area());   // 25

// Calling static methods through an instance does not work
// sq.fromSquare(2);   // TypeError

Static methods are a natural home for factory functions, type guards, and pure helpers. We cover them in detail in Static Members.

this inside methods

Inside an instance method, this is whatever was on the left of the dot at the call site. Detach the method from its instance and this is lost.

JS
class Counter {
  count = 0;
  inc() { this.count++; }
  read() { return this.count; }
}

const c = new Counter();
c.inc(); c.inc();
console.log(c.read());   // 2

const inc = c.inc;
try { inc(); } catch (e) { console.log("lost this:", e.message); }
Class bodies are always strict
Method bodies in a class run in strict mode automatically. A detached method with no receiver has `this === undefined` — it throws on `this.count++` rather than quietly writing to a global. That early failure is a feature.
Three ways to preserve `this` on a callback
  • Wrap at the call site: setTimeout(() => c.inc(), 100). The arrow keeps a reference to the instance.

  • Bind: setTimeout(c.inc.bind(c), 100). Creates a new bound function each time.

  • Define the method as a field arrow: inc = () => { this.count++; }. Each instance gets its own arrow with lexical this — slightly more memory, much less bookkeeping.

JS
class Counter {
  count = 0;
  inc = () => { this.count++; };   // class field, arrow — pre-bound
}

const c = new Counter();
const f = c.inc;
f(); f();
console.log(c.count);   // 2
Computed method names

Methods can also use computed names with brackets. Useful for implementing well-known symbols like Symbol.iterator.

JS
class Range {
  constructor(from, to) { this.from = from; this.to = to; }

  *[Symbol.iterator]() {
    for (let i = this.from; i <= this.to; i++) yield i;
  }
}

for (const n of new Range(1, 3)) console.log(n);
1
2
3
Async and generator methods

JS
class Loader {
  async fetchUser(id) {
    const res = await fetch(`/api/users/${id}`);
    return res.json();
  }

  *items() {
    yield 1; yield 2; yield 3;
  }
}

async and * work as you would expect inside class method definitions — the same as on regular functions.

Methods vs fields
A method defined the normal way is on the prototype and shared. An arrow assigned to a class field is *per instance*. The first is more idiomatic for OO designs; the second is convenient for React-style components that pass methods as event handlers.
One sentence
Instance methods live on the prototype and respect dynamic `this`; static methods live on the class and act as namespaced helpers. The syntax is one word apart.