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 shared counts, shared lists, and tools that need no object.
- 02Know when class setup runsSay when static fields and static blocks run, and show that they run only once.
- 03Write clear factory methodsWrite static methods such as
fromJSONandfromHexthat make objects, each named for its kind of input.
Members that belong to the class
A class is a plan for making objects. Each object made from the class is called an instance. For example, one user, one ticket, or one color.
A member is a field (a stored value) or a method (a function) that a class defines. Most members belong to each instance. A static member is different: it belongs to the class itself. You use it with the class name, like ClassName.member. You don't use it with an instance, like instance.member.
Why can a class hold things? In JavaScript, a class is a special kind of function, and a function is an object. So a class can have its own properties, like any object. A static field is one of those properties. A static method is a function stored as one of those properties.
Think of a car factory. The factory keeps a count of how many cars it has built. It also keeps the steps for building a car from a serial number. Each car keeps its own mileage and has its own horn. You would not write the factory's total inside every car. You would not write one car's mileage on the factory wall. Static members are what the factory keeps for itself.
- In real life: The factory's count of cars built
- In JavaScript:
Car.built, a static field - In real life: The factory's steps for building a car from a serial number
- In JavaScript:
Car.fromSerial(...), a static method - In real life: One car's mileage
- In JavaScript:
car.mileage, an instance field - In real life: One car's horn button
- In JavaScript:
car.honk(), an instance method
The table compares the two kinds of member. Read each row as a question, then the answer for each kind.
| Question | Static member | Instance member |
|---|---|---|
| How do you call it? | With the class name: User.create(raw). | With one object: user.save(). |
| Where does JavaScript find it? | On the class itself. | On the object, or on the shared methods of its class. |
What is this inside a method? | Usually the class, such as User. | One object, such as user. |
| What is it good for? | Counts, lists, setup, and ways to make objects. | Work that reads or changes one object's data. |
In this lesson you will:
- count objects with one shared field,
- watch class setup code run before any object exists,
- see that a subclass gets the static members of its parent class,
- write methods that make objects, like
Temperature.fromFahrenheit(98.6).
Static fields and methods
STEP THROUGHTo make a static member, write the word static in front of a field or method inside the class. JavaScript then puts that member on the class. It does not copy the member onto the objects you make later.
class Cart { static itemCount = 0; constructor(item) { this.item = item; Cart.itemCount += 1; }}const tea = new Cart("tea");const book = new Cart("book");console.log(Cart.itemCount);console.log(tea.item);Line 2 makes one itemCount field on the class Cart. It starts at 0. Line 5 runs every time you write new Cart(...). It adds 1 to that same field. Lines 8 and 9 make two carts, so the count becomes 2.
Line 10 prints Cart.itemCount, which is 2. Line 11 prints tea.item, which is tea. Each cart has its own item, but there is only one itemCount.
Cart holds the only itemCount. Each new Cart() adds 1 to it, so it ends at 2, while each object keeps just its own item.The next example adds a static method too. Press Step to walk through it one line at a time.
Step through two new tickets. Watch one static field count both of them. Then see that neither ticket has the static method.
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);This is the usual shape. A static field stores a value that all objects share. The constructor or a method changes it. The last line shows something important: ada.summary is undefined. The method summary starts with static, so it lives on Ticket, not on ada.
A static field can change. Ticket.sold starts at 0, then becomes 1, then 2. The word static means "on the class". It does not mean "cannot change". To protect a value, use a private field (next lesson) or a naming rule your team agrees on.
class Ticket { static summary() { return "ready"; }}const ticket = new Ticket();console.log(ticket.summary());Line 7 fails with a TypeError. The object ticket has no summary, so ticket.summary is undefined, and you can't call undefined. The fix is to call it on the class: Ticket.summary().
A static method is also handy when you don't need an object at all. JavaScript's own built-in tools work this way:
Math.max(3, 10);Date.parse("2026-09-25");Array.from("JS");Object.fromEntries([["role", "admin"]]);Math.maxfinds the biggest number. You never writenew Math(); you just use the tool.Date.parseturns date text into a number: the milliseconds since 1970.Array.frommakes a new array from something else, here the letters of"JS".Object.fromEntriesmakes a new object from a list of pairs.
The last two make new values from a different kind of input. Remember that idea; it comes back in factory methods at the end of the lesson.
User.fromJSON(raw)reads data and returns a new useruser.fullName()readsthis.firstNameandthis.lastNameTicket.soldcounts every ticket made so farcar.mileageis different for every carPlugin.registrykeeps a list of all plugins by namearticle.render()shows this article's title and body
Put each member on the class or on one object. Ask: does it need one object's data, or does it belong to the whole class?
Static blocks: setup that runs once
INTERACTIVEA static field can hold a simple starting value. Sometimes the setup needs several lines of code, like filling a list. For that, use a static block: the word static followed by curly braces, static { ... }, inside the class.
A static block runs exactly once, while JavaScript is making the class. It finishes before the code after the class runs.
class Parser { static formats = []; static { Parser.formats.push("json", "text"); }}console.log(Parser.formats.join(", "));Line 2 makes an empty list, Parser.formats. Lines 3 to 5 are the static block. It adds "json" and "text" to the list. Line 7 prints json, text. If you made a Parser object later, the block would not run again.
Before a factory builds its first car, someone opens the doors, sets up the machines, and writes the first notes. This happens once, not once per car. A static block is that opening-day setup for a class.
- In real life: Turn on and set up the machines
- In JavaScript: Run the
static { ... }block - In real life: Write the first notes on the factory board
- In JavaScript: Fill the class's static fields
- In real life: Build the first car, later
- In JavaScript: Call
new ClassName(...)later
Now step through a longer example. Watch for two things: the static block prints before after class definition, and before the constructor prints Ada #1.
Before you press Step, guess which line prints first. Static fields and blocks run while the class is being made, 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);new Workshop("Ada").Static fields and static blocks run from top to bottom, in the order you wrote them. A block can use the static fields written above it. The clearest code puts the fields first and the block that fills them right after.
A subclass gets the static members too
STEP THROUGHA subclass is a class made with extends, like class Admin extends Model. The class it extends, Model, is the parent class.
extends makes two links:
- Each
Adminobject can use the instance methods ofModel. - The class
Adminitself can use the static members ofModel.
You can check the second link with Object.getPrototypeOf(Admin) === Model. It gives true. Object.getPrototypeOf(x) returns the object that x falls back to when it doesn't have a property.
class Model { static create(name) { return new this(name); } constructor(name) { this.name = name; }}class Admin extends Model {}const admin = Admin.create("Asha");console.log(admin.constructor === Admin);Line 9 makes Admin with no members of its own. Line 10 calls Admin.create("Asha"). Admin has no create, so JavaScript follows the link and finds it on Model.
Inside a static method, this is the class you called it on. Here that is Admin. So on line 3, new this(name) means new Admin(name). Line 11 prints true.
Admin has no create() of its own, so JavaScript finds it on Model. The call still starts from Admin, so this is Admin and the result is an Admin.Guess the three printed values first. The key question: when a subclass calls its parent's 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 key is this. When you call User.create("Ada"), the method runs with this set to User, even though it was written in BaseModel. Because the parent's method says new this(name), each subclass gets objects of its own kind.
This lesson covers public static members, which any code can use. Private static members have names that start with #, and only code inside the class can use them. The next lesson, Private fields & methods, explains them.
Factory methods: named ways to make objects
INTERACTIVEA class has only one constructor. But you may want to make the same kind of object from different inputs. A factory method is a static method that makes an object and returns it. Its name tells the reader what kind of input it takes.
class Temperature { constructor(celsius) { this.celsius = celsius; } static fromFahrenheit(value) { return new Temperature((value - 32) * 5 / 9); }}const normal = Temperature.fromFahrenheit(98.6);The constructor on lines 2 to 4 stores a temperature in Celsius. The factory method on lines 5 to 7 takes Fahrenheit, changes it to Celsius, and then calls new Temperature(...). Line 9 uses it, so normal.celsius is 37.
new Temperature(37).Compare it with new Temperature(98.6). A reader can't tell if 98.6 is Celsius or Fahrenheit. Temperature.fromFahrenheit(98.6) says it clearly. A factory method can also:
- check the input and throw an error if it is wrong,
- tidy the input into the shape the constructor expects,
- pick which subclass to make,
- give back an object it made earlier, instead of a new one.
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 object. Its normal method toString() gave rgb(255, 136, 0).
Color class with two static factory methods. Pick Hex or RGB, then type a value. Try #336699, steelblue, 256, and a negative number to see each check fail or pass.Try wrong inputs: steelblue, #12, or a number outside 0 to 255. Each factory method throws an error before any Color object is made. The method toString() is a normal instance method, because it reads one color's own red, green, and blue.
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 + ")"; }}console.log(User.fromJSON('{"name":"Asha"}').label());JSON is a text format for data. On line 7, JSON.parse(text) turns that text into an object. Line 8 makes the user. The text has no role, so data.role is undefined, and ?? "member" uses "member" instead. Line 14 prints Asha (member).
Where you'll use this
Use a static member when the job belongs to the class as a whole, not to one object. Each row of the table is one common job, a real example, and why static fits it.
| Job | Example | Why it belongs on the class |
|---|---|---|
| A set of tools | Math.max(1, 5) | There is no single Math object to make. You just use the tool. |
| Turn one kind of data into another | Date.parse(text), Array.from(items) | The method makes a new value from the input. |
| A shared list | Formatter.register(name, fn) | Every object should use the same list. |
| Remember earlier results | Icon.load(name) reuses icons it loaded before | One shared store saves doing the same work twice. |
| Make objects | Color.fromHex("#336699") | The method name says what kind of input it takes. |
Use an instance member when the work depends on one object's own data. Use a static member when the question is about the whole class, or when no object exists yet.
Common misconceptions
"Static means the value can't change."
Static means "on the class". Ticket.sold changed from 0 to 2. To stop other code changing a value, use a private static field.
"Objects can call static methods."
They can't. Ticket.summary() works, but ada.summary is undefined. The method is on the class, not on the object.
"A static block runs every time I make an object."
The constructor runs for each new. A static block runs once, while the class is being made.
"A parent's static method always makes parent objects."
If the method uses new this(), calling it on a subclass makes a subclass object. Inside a static method, this is the class you called it on.
"Factory methods are only about style."
They also make code safer. The name says what input the method takes. The method can check that input in one place, and it can return a saved object or a subclass without the calling code changing.
Practice: static members
5 EXERCISESDo these in order. Guess the answer first, then check it.
Read the code and guess the number it prints. Then write the same 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 new Session() runs Session.count += 1. There are three, so the field ends at 3.
Don't run 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);extends links Admin to Model, so the first line prints true. The method is called as Admin.create, so this.name prints Admin. Then new this(name) makes an Admin, so the last line prints true.
fromJSON factory methodUse the code as a model. The static method reads JSON text and returns a normal User object, which has the normal method 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());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 says the input is JSON text. It reads the text, uses member if there is no role, and returns new User(...). The text has the role admin, so it prints Ada (admin).
This code puts one-time setup in static { ... }. Guess the number it prints at the end.
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 being made. It sets loaded to 1 and fills cache. Making objects later does not run it again.
A teammate wrote user.create("Ada") and got a TypeError. What should they write 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"). It returns a User object, so user.name prints Ada.
Quiz: check your understanding
7 QUESTIONSEach answer comes with a short reason. Read the reason even when you got it right.
Question 1 of 7Where does a static field live?
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 7This code checks the link that
extendsmakes between two classes. What does it print?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 write
Temperature.fromFahrenheit(98.6)instead ofnew 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 live on the class itself.
- Objects made from the class don't get static members.
- Static fields and static blocks run once, top to bottom, while the class is being made.
- A subclass can use its parent's public static members. Inside a static method,
thisis the class you called it on. - Factory methods are static methods with clear names that check their input and make objects.
Remember the one-liner.
A static member belongs to the class itself: use it for shared data, one-time setup, and named ways to make objects.
Up next: Private fields & methods. You'll learn names that start with #, which only code inside the class can use, including private static members.