Static Members Belong to the Class, Not the Instance
Most methods you write on a class operate on a specific instance — a particular user, a particular circle. Static members are different. They belong to the class itself. You call them on the class name, not on an instance.
double lives on MathUtils, not on instances of it. Creating an instance doesn't help — m.double is undefined. This is the opposite of regular methods, which live on instances (technically, on the prototype) and are invisible to the class.
The mental model: the class keyword creates two things — a set of instance methods (used by new MathUtils()) and a set of static methods (used by MathUtils directly). The static keyword chooses which bucket a member goes into.
The Classic Use: Factory Methods
The most common reason to write a static method is an alternative constructor. The real constructor takes whatever arguments it takes, but you often want other ways to build an object — from JSON, from a database row, from a URL.
fromJSON isn't using a user — it's producing one. That's exactly when a static method fits. The alternative would be a free-floating parseUser function, but keeping it on the class groups related behavior together and makes the intent obvious at the call site.
Static Properties
You can also attach data to the class itself:
Circle.PI is shared across every circle — it's a constant on the class, not copied into each instance. Inside instance methods, you reference it through the class name (Circle.PI), not through this.
Static properties are handy for configuration, caches shared across instances, counters, and class-level constants.
this Inside a Static Method Is the Class
Inside a regular method, this is the instance. Inside a static method, this is the class:
this.count inside increment means Counter.count. That might feel odd at first, but it's what makes inheritance of static methods work — this refers to whichever class you called the method on, not the class that defined it.
Static Methods and Inheritance
Static methods are inherited by subclasses. And because this points at the class the method was called on, factory methods automatically produce the right subclass:
Animal.create uses new this(name). When you call Dog.create("Rex"), this is Dog, so new this(name) builds a Dog. Writing new Animal(name) there instead would always produce an Animal, breaking the pattern. This is the main reason this in static methods points to the class.
Static vs. Instance: a Worked Contrast
Here's the same kind of logic split between the two styles:
Both do the same math. The instance version reads data from this.celsius. The static version takes its input as an argument. Pick the instance method when the operation is "something this object does"; pick the static method when it's "something this class knows how to compute, given inputs."
Static Blocks for Setup
Sometimes initializing a static property needs more than a single expression — a loop, a conditional, multiple dependent values. That's what static blocks are for:
A static { ... } block runs once, when the class is defined. Inside it, this is the class. Use it for multi-step setup; for a single assignment, a plain static field is clearer.
Private Static Members
Static fields can be private too, using the # prefix. They're only accessible from inside the class:
#nextId is sealed inside the class. Outside code can call IdGenerator.next() but can't touch or reset the counter. Private fields get their own page shortly, but it's worth knowing the static and # combo works.
When Not to Reach for static
Static methods are a nice way to group helpers, but they're not a reason to turn every utility into a class. If you have a file full of independent functions, export the functions — don't wrap them in a class with all-static methods just to namespace them. A module already does that job, and it does it more cleanly.
Reach for static when:
- The function genuinely belongs to a class (factory, converter, validator for that type).
- You need a piece of state shared across all instances of that class.
- A subclass might want to override or inherit the behavior.
Otherwise, a plain function is the simpler tool.
Next: Private Fields
You saw #nextId briefly — that's JavaScript's private field syntax. It works for both instance and static members, and it's the modern way to hide implementation details inside a class. That's up next.
Frequently Asked Questions
What is a static method in JavaScript?
A static method belongs to the class itself, not to instances of the class. You declare it with the static keyword and call it on the class: MyClass.doThing(). Instances don't have access to it through this.doThing() — only the class does.
When should I use a static method instead of an instance method?
Use a static method when the function is related to the class but doesn't need to read or modify a specific instance. Common cases: factory methods like User.fromJSON(...), utility helpers like Math.max, and constants that belong to the class as a namespace. If the method needs this to point at an instance, it's an instance method.
Can static methods access instance properties?
Not directly. Inside a static method, this refers to the class, not an instance, so this.name reads a static property, not an instance field. If you need instance data, pass the instance in as an argument: static summarize(user) { return user.name; }.
Are static methods inherited in JavaScript?
Yes. When a subclass extends a parent class, the parent's static methods are available on the subclass too. Inside a static method, this refers to whichever class you called it on, which is what makes static factory methods work correctly across subclasses.