Static members & static blocks
Learn how JavaScript class static fields, static methods, static initialization blocks, inherited statics, and factory methods put behavior on the class itself instead of each instance.
- 01Put data on the classUse static fields and methods for counters, registries, and utilities.
- 02Trace class definition timePredict when static fields and static blocks run, and prove they run once.
- 03Design clear factoriesWrite named constructors such as
fromJSONandfromHexwithout overloadingconstructor.
Class-level members
A class usually teaches JavaScript how to build instances: one article, one color, one user, one ticket. A static member is different. It belongs to the class itself. You use it as ClassName.member, not as instance.member.
This connects back to Functions are objects: a class is a special kind of function, and functions can have properties. A static field is a property on that class function. A static method is a function stored there. The Class basics & extends lesson gives you the instance side; this lesson gives you the class side.
Imagine a car factory. The factory keeps a production counter and a checklist for turning a serial number into a car. Each car keeps its own mileage and tire pressure. You would not store total cars built inside every car, and you would not store one car’s mileage on the factory wall. Static members are the factory wall.
- In real life: The car factory's production counter
- In JavaScript:
Car.builtstatic field - In real life: The factory's quality-check recipe
- In JavaScript:
Car.fromSerial(...)static factory - In real life: One car’s mileage
- In JavaScript:
car.mileageinstance field - In real life: One car’s horn button
- In JavaScript:
car.honk()instance method
Where the analogy stops: A real factory and car are separate physical things. In JavaScript the class is also the constructor function used by new, so the factory and blueprint live in one value.
| Question | Static | Instance |
|---|---|---|
| How you call it | User.create(raw) | user.save() |
| Where it is found | On the class constructor function | On the object or its prototype |
What this usually is | The class, such as User | One object, such as user |
| Good for | Counters, registries, parsers, factories, utilities | Behavior that reads or changes one object’s state |
In this lesson you will watch class setup run, count instances with a shared field, prove statics are inherited, and build named factory methods like Temperature.fromFahrenheit(98.6).
Static methods & fields
STEP THROUGHAdd the static keyword inside a class body to create a class-level field or method. The name is read from the class: Ticket.sold and Ticket.summary(). It is not copied onto ada or lin when you create tickets.
Step through two instances. Watch one static field count both of them, then prove the method is not on either instance.
script
class Ticket { constructor(name) { this.name = name; Ticket.sold += 1; } static summary() { return Ticket.sold + " tickets sold"; }}const ada = new Ticket("Ada");const lin = new Ticket("Lin");console.log(Ticket.summary());console.log(ada.summary);The counter example shows the usual shape: a static field stores shared state, and a constructor or method updates it. The output also proves a common gotcha: ada.summary is undefined. If the member starts with static, call it on the class.
A static field can change. Ticket.sold starts at 0 and becomes 1, then 2. The word static means “on the class,” not “unchangeable.” Use readonly patterns, private fields, or conventions when you need protection.
Static methods are also useful when no instance is needed. JavaScript’s built-ins use this style all the time:
Math.max(3, 10);Date.parse("2026-09-25");Array.from("JS");Object.fromEntries([["role", "admin"]]);Math.max is a Math-like utility: you never write new Math(). Array.from and Object.fromEntries are named entrances that create values from other shapes of data. That naming pattern becomes important when we reach factory methods.
User.fromJSON(raw)parses data and returns a new useruser.fullName()readsthis.firstNameandthis.lastNameTicket.soldcounts every ticket that has been constructedcar.mileageis different for every carPlugin.registrystores plugins by namearticle.render()uses this article's title and body
Decide whether each member belongs on the class or on one object. Ask: does it need one object’s state, or the whole class?
Static initialization blocks
INTERACTIVEStatic fields handle simple setup. A static block handles setup that needs several statements: fill a registry, validate a table, connect related static fields, or choose a fallback. It runs while the class definition is evaluated, exactly once, before code after the class continues.
Before a factory makes its first product, someone opens the doors, calibrates machines, and writes the first checklist. That is not a per-product step. A static block is the class’s opening-day setup.
- In real life: Unlock the factory and calibrate machines
- In JavaScript: Run the
static { ... }block - In real life: Write the initial production notes
- In JavaScript: Fill class-level fields and registries
- In real life: Build the first product later
- In JavaScript: Call
new ClassName(...)later
Where the analogy stops: Opening-day setup might involve people and clocks. A static block is deterministic code that runs as part of evaluating the class; it should not depend on browser-only values during server rendering.
Step through this recording slowly. Notice that the static block logs before after class definition, and before the constructor logs Ada #1.
Before stepping, predict which message prints first. Static fields and blocks run while the class is defined, before any constructor call.
script
class Workshop { static setupLog = []; static { Workshop.setupLog.push("factory setup"); console.log("static block ran"); } constructor(name) { this.name = name; this.id = Workshop.nextId++; console.log(name + " #" + this.id); }}console.log("after class definition");const first = new Workshop("Ada");console.log(Workshop.setupLog.length);Static fields and blocks run in the order they appear. A block can read earlier static fields and write later static fields once they exist, but the cleanest code puts fields first, then the block that prepares them. Avoid doing reader-specific work during render. If setup depends on window, locale formatting, random numbers, or time, compute it on the client after the page mounts or on a user action.
Inheriting statics
STEP THROUGHextends links two sides at once. Instances of Sub can find methods from Base.prototype, and the subclass constructor Sub can find static members on Base. That is why Object.getPrototypeOf(Sub) === Base is true.
Predict the three printed values. The key question: when a subclass calls an inherited static method, what is this?
script
class BaseModel { static create(name) { console.log(this.name); return new this(name); } constructor(name) { this.name = name; }}console.log(Object.getPrototypeOf(User) === BaseModel);const user = User.create("Ada");console.log(user instanceof User);The important part is this. Inside a static method, this is the class used for the call. When you call User.create("Ada"), the inherited method runs with this === User. That lets the base method return new this(name), so subclasses get instances of themselves.
Public inherited statics are covered here. Private static fields and methods use # names and have stricter access rules. The next lesson, Private fields & methods, covers that carefully.
Factory methods
INTERACTIVEJavaScript does not support multiple constructors with different names. A class has one constructor. Static factory methods give you named entrances instead. The name tells readers what kind of input they are handing over.
class Temperature { constructor(celsius) { this.celsius = celsius; } static fromFahrenheit(value) { return new Temperature((value - 32) * 5 / 9); }}const normal = Temperature.fromFahrenheit(98.6);new Temperature(98.6) is ambiguous: is that Celsius or Fahrenheit? Temperature.fromFahrenheit(98.6) says exactly what the input means. Factories can validate input, normalize data, choose a subclass, use a cache, or return an existing instance.
class Color { constructor(red, green, blue) { this.red = red; this.green = green; this.blue = blue; } static fromHex(hex) { if (!/^#[0-9a-f]{6}$/i.test(hex)) throw new Error("Use #RRGGBB"); return new Color( parseInt(hex.slice(1, 3), 16), parseInt(hex.slice(3, 5), 16), parseInt(hex.slice(5, 7), 16) ); } static fromRGB(red, green, blue) { for (const value of [red, green, blue]) { if (!Number.isInteger(value) || value < 0 || value > 255) throw new Error("RGB values are 0-255"); } return new Color(red, green, blue); } toString() { return "rgb(" + this.red + ", " + this.green + ", " + this.blue + ")"; }}Color.fromHex("#ff8800")Color.fromHex("#ff8800") returned a Color instance. Its normal instance method toString() produced rgb(255, 136, 0).
Color class with two named static factories. Try #336699, steelblue, 256, and negative RGB numbers to see validation paths.Try invalid values: steelblue, #12, or RGB values outside 0–255. The factory rejects them before an invalid Color object is created. The ordinary instance method toString() still belongs on each color object because it reads that object’s red, green, and blue fields.
Where you’ll use this
Static members are common whenever the class itself has a job, not just each instance. Professional code uses them for utilities, caches, registries, parsers, and creation helpers.
| Use | Example | Why static fits |
|---|---|---|
| Utility namespace | Math.max(1, 5) | There is no meaningful Math instance. |
| Parser or converter | Date.parse(text), Array.from(items) | The method creates or computes a value from input. |
| Registry | Formatter.register(name, fn) | Every instance should consult the same class-level list. |
| Cache | Icon.load(name) reuses stored icons | A shared lookup prevents duplicated work. |
| Factory | Color.fromHex("#336699") | The method name documents the input format. |
Use instance members for behavior that depends on one object’s fields. Use static members when the question starts with “the whole class,” “before an instance exists,” or “which entrance should construct this?”
Common misconceptions
“Static means the value can’t change.”
Static means class-level. Ticket.sold changed from 0 to 2. Use a constant naming convention or a private static field when you want to restrict writes.
“Instances can call static methods.”
Not by default. Ticket.summary() works; ada.summary is undefined because the method is stored on the class, not the instance.
“Static blocks run when I create objects.”
Constructors run for each new. Static blocks run once when the class is defined. They are setup code, not per-instance code.
“Inherited static methods always create base instances.”
If the method uses new this(), a subclass call creates the subclass. this inside a static method is the class used for the call.
“Factory methods are just style.”
They are style plus safety. A factory name documents the input format, centralizes validation, and can return cached or specialized objects without changing callers.
Practice: static members
5 EXERCISESTry these in order. Predict first, then check against real JavaScript.
Read the code and predict the printed count. Then adapt the pattern in your own editor with a class name you choose.
class Session {
static count = 0;
constructor() {
Session.count += 1;
}
}
new Session();
new Session();
new Session();
console.log(Session.count);class Session {
static count = 0;
constructor() {
Session.count += 1;
}
}
new Session();
new Session();
new Session();
console.log(Session.count);The static field starts at 0. Each constructor call runs Session.count += 1, so after three instances the class field holds 3.
Without running the code, type the three printed values in order.
class Model {
static create(name) {
console.log(this.name);
return new this(name);
}
constructor(name) {
this.name = name;
}
}
class Admin extends Model {}
console.log(Object.getPrototypeOf(Admin) === Model);
const admin = Admin.create("Ada");
console.log(admin instanceof Admin);The class-side prototype link is real, so the first log is true. The inherited static method is called as Admin.create, so this.name logs Admin. new this(name) then creates an Admin, making the last log true.
fromJSON factoryUse the starter as a model: a static factory parses JSON text and returns a normal instance with instance methods.
class User {
constructor(name, role) {
this.name = name;
this.role = role;
}
static fromJSON(text) {
const data = JSON.parse(text);
return new User(data.name, data.role ?? "member");
}
label() {
return this.name + " (" + this.role + ")";
}
}
const user = User.fromJSON('{"name":"Ada","role":"admin"}');
console.log(user.label());class User {
constructor(name, role) {
this.name = name;
this.role = role;
}
static fromJSON(text) {
const data = JSON.parse(text);
return new User(data.name, data.role ?? "member");
}
label() {
return this.name + " (" + this.role + ")";
}
}
const user = User.fromJSON('{"name":"Ada","role":"admin"}');
console.log(user.label());User.fromJSON names the input format, parses the text, supplies a default role, and returns new User(...). The final label is Ada (admin).
This exercise moves one-time setup into static { ... }. Predict the final count.
class Settings {
static loaded = 0;
static cache;
static {
Settings.loaded += 1;
Settings.cache = { mode: "safe" };
console.log("read config once");
}
}
new Settings();
new Settings();
console.log(Settings.loaded);class Settings {
static loaded = 0;
static cache;
static {
Settings.loaded += 1;
Settings.cache = { mode: "safe" };
console.log("read config once");
}
}
new Settings();
new Settings();
console.log(Settings.loaded);The static block runs once while Settings is defined, increments loaded to 1, and prepares the cache. Creating instances later does not rerun it.
A teammate wrote user.create("Ada") and got a TypeError. What expression should they call instead?
class User {
static create(name) {
return new User(name);
}
constructor(name) {
this.name = name;
}
}
const user = User.create("Ada");
console.log(user.name);The fixed code calls User.create("Ada"). The factory returns a User instance, so logging user.name prints Ada.
Quiz: check your understanding
7 QUESTIONSEvery answer explains itself. Read the explanations even when you get the first try right.
Question 1 of 7Where is a static field stored?
Choose an answer to see the explanation.
Question 2 of 7What does snippet 1 print?
Read the code, then predictclass Counter { static value = 0; constructor() { Counter.value += 1; } } new Counter(); new Counter(); console.log(Counter.value);Choose an answer to see the explanation.
Question 3 of 7When does a static block run?
Choose an answer to see the explanation.
Question 4 of 7What does snippet 2 print?
Read the code, then predictclass Base { static create() { console.log(this.name); } } class Child extends Base {} Child.create();Choose an answer to see the explanation.
Question 5 of 7Which line proves subclasses inherit statics through the class side?
Read the code, then predictclass Base {} class Sub extends Base {} console.log(Object.getPrototypeOf(Sub) === Base);Choose an answer to see the explanation.
Question 6 of 7Why use
Temperature.fromFahrenheit(98.6)instead of overloadingnew Temperature(98.6)?Choose an answer to see the explanation.
Question 7 of 7What does snippet 3 print?
Read the code, then predictclass Tool { static create() { return new Tool(); } } const tool = new Tool(); console.log(tool.create);Choose an answer to see the explanation.
Key takeaways
- Static fields and methods belong to the class constructor function.
- Instances do not automatically receive static members.
- Static blocks run once, in order with static fields, while the class is defined.
- Subclasses inherit public statics; inside a static method,
thisis the class used for the call. - Factory methods are named entrances that validate and explain different ways to create instances.
Remember the one-liner.
A static member is a property of the class itself: use it for class-wide data, setup, and named ways to create objects.
Up next: Private fields & methods, including private instance fields and private statics with # names.