Skip to content
JavaScript js classes 4 min read

Static Methods & Properties

Most class members belong to instances — each object created with new gets its own copy. Static members are different: they belong to the class itself, not to any instance. You call them on the constructor (MathUtils.clamp(...)) rather than on an object, which makes them perfect for utility helpers, factory methods, and state shared across every instance. This page covers static methods, static fields, static blocks, and the patterns where they shine.

What “static” means

The static keyword placed before a method or field definition attaches that member to the class constructor rather than to its prototype. As a result, instances cannot see it, and the class can.

class Circle {
  static PI = 3.14159;

  static areaFromRadius(r) {
    return Circle.PI * r * r;
  }
}

console.log(Circle.PI);                 // 3.14159
console.log(Circle.areaFromRadius(2));  // 12.56636

const c = new Circle();
console.log(c.PI);                      // undefined

Output:

3.14159
12.56636
undefined

Notice that c.PI is undefined — the field lives on Circle, not on the instance. Static members are accessed through the class name.

Utility methods

The most common use of static methods is to group related pure functions under a namespace. The built-in Math and Object objects work this way, and you can model your own helpers after them.

class StringUtils {
  static capitalize(str) {
    if (!str) return str;
    return str[0].toUpperCase() + str.slice(1);
  }

  static slugify(str) {
    return str
      .toLowerCase()
      .trim()
      .replace(/[^\w\s-]/g, "")
      .replace(/\s+/g, "-");
  }
}

console.log(StringUtils.capitalize("hello"));
console.log(StringUtils.slugify("  My Great Post!  "));

Output:

Hello
my-great-post

These methods never need instance data, so making them static keeps the call site clear and avoids pointless object creation.

Factory methods

A static method can act as a named alternative constructor. This is cleaner than overloading a single constructor with many optional arguments, and it gives each creation path a descriptive name.

class User {
  constructor(name, role) {
    this.name = name;
    this.role = role;
  }

  static guest() {
    return new User("Guest", "viewer");
  }

  static fromJSON(json) {
    const { name, role } = JSON.parse(json);
    return new User(name, role);
  }
}

const guest = User.guest();
const admin = User.fromJSON('{"name":"Ada","role":"admin"}');

console.log(guest.role);
console.log(admin.name);

Output:

viewer
Ada

Inside a static method, this refers to the class itself, so return new this(...) lets subclasses produce instances of their own type instead of the parent’s.

Shared state and counters

Because a static field exists once per class, it is ideal for state every instance should share — caches, registries, or counters.

class Widget {
  static count = 0;

  constructor(label) {
    this.label = label;
    this.id = ++Widget.count;
  }

  static reset() {
    Widget.count = 0;
  }
}

const a = new Widget("A");
const b = new Widget("B");

console.log(a.id, b.id);      // 1 2
console.log(Widget.count);    // 2

Output:

1 2
2

Every new Widget() increments the same Widget.count, giving each widget a unique sequential id.

Static blocks

Sometimes a static field needs more than a single expression to initialize — perhaps a loop, a try/catch, or several dependent values. A static {} block (ES2022) runs once when the class is defined and can access private static members.

class Config {
  static settings;
  static #source = "env";

  static {
    const defaults = { theme: "dark", retries: 3 };
    const overrides = { retries: 5 };
    Config.settings = { ...defaults, ...overrides };
    console.log(`Loaded config from ${Config.#source}`);
  }
}

console.log(Config.settings);

Output:

Loaded config from env
{ theme: 'dark', retries: 3, retries: 5 }

You can declare multiple static blocks; they execute in source order, interleaved with static field initializers.

Instance vs. static comparison

AspectInstance memberStatic member
Belongs toEach objectThe class itself
Called viaobj.method()Class.method()
this refers toThe instanceThe class
Typical usePer-object data & behaviorUtilities, factories, shared state
Inherited byInstancesSubclasses (via the constructor)

Static members are inherited through the class chain: a subclass can call a parent’s static method on itself, and this will resolve to the subclass.

class Animal {
  static create(name) {
    return new this(name);
  }
  constructor(name) { this.name = name; }
}

class Dog extends Animal {}

const d = Dog.create("Rex");
console.log(d instanceof Dog);   // true

Output:

true

Best Practices

  • Reach for static methods only when the logic does not depend on instance state — otherwise it belongs on the prototype.
  • Use static factory methods (fromJSON, of, create) to give creation paths clear, intention-revealing names.
  • Prefer new this(...) over new ClassName(...) inside static methods so subclasses inherit the correct behavior.
  • Keep shared mutable static state minimal; it behaves like a global and can complicate testing and concurrency.
  • Combine static #private fields with static blocks to encapsulate one-time setup logic safely.
  • Don’t try to access static members through this in an instance method — reference the class name (or this.constructor) instead.
Last updated June 1, 2026
Was this helpful?