skip to content

Static Members and Static Blocks

Members that live on the constructor rather than on instances, including static fields, static private members and static initialization blocks. Interviewers check that you know statics are inherited by subclasses and that `this` inside them refers to the class.

part ofJavaScriptoverview, primer and where to startread it →
on this pageshow

questions

4

In a JavaScript class, what does the `static` keyword do — which object does the member live on, and can you reach it from an instance?

level: juniorimportance: must knowfreq 72%

answer

  1. one copy, on the class object
  2. instance lookup never visits the class
  3. reach it through the class name
  4. instance.staticThing is undefined

basics

~20 s

static installs a method or field on the class constructor itself instead of on instances. You call it as ClassName.member; reading it from an instance gives undefined, because the constructor is not on the instance's prototype chain.

solid answer

~40 s

A class declaration produces two linked objects: the constructor `C` and `C.prototype`. Instance members live on `C.prototype` (methods) or directly on each instance (fields set in the constructor); `static` members live on `C` itself. So `Circle.area(2)` works while `new Circle(2).area` is `undefined`, because property lookup on an instance walks instance → `C.prototype` → `Object.prototype`, and `C` is nowhere in that chain. Inside a static method `this` is the class, not an instance, so a static cannot touch per-instance state unless you hand it an instance. Statics are the right home for factories and named constructors (`Array.from`, `Promise.resolve`, `Object.keys` are all built-in examples), shared constants, and parsing helpers such as `static fromJSON(json)`.

code

javascript · 11 lines
javascript
class Circle {
  static PI = 3.14159;
  static area(r) { return this.PI * r * r; }
  constructor(r) { this.r = r; }
  describe() { return `r=${this.r}`; }
}

console.log(Circle.area(2));            // 12.56636
console.log(new Circle(2).area);        // undefined
console.log(new Circle(2).describe());  // "r=2"
console.log(Object.keys(Circle));       // ["PI"] — methods are non-enumerable

go deeper

for a junior

Be able to say plainly that a static member belongs to the class object and is called as ClassName.member, and that reading it off an instance yields undefined. Name one everyday example such as Array.from.

for a middle

Explain the mechanism: instance lookup walks the instance then C.prototype then Object.prototype, and the constructor is not on that chain. Know that static methods are non-enumerable while public static fields are enumerable.

for a senior

Show judgment about what belongs on the class: factories and named constructors, type-level operations, shared constants — and be ready to say why a mutable static counter or cache is shared program-wide and how you would test code that depends on it.

for a principal

Own the API-shape argument: when a static factory beats an exported function or a plain constructor, what a static surface costs in substitutability, and how you would keep class-level state from becoming an untestable global in a codebase many teams import.

## Two objects, not one Every class declaration creates two linked objects. There is the constructor function itself — the binding you get from `class Circle { ... }` — and there is `Circle.prototype`, the object every instance receives as its prototype. `static` is the keyword that decides which of those two objects a member is installed on. ```js class Circle { static PI = 3.14159; // own property of Circle static area(r) { return this.PI * r * r; } // own property of Circle constructor(r) { this.r = r; } // own property of each instance describe() { return `r=${this.r}`; } // property of Circle.prototype } Circle.area(2); // 12.56636 new Circle(2).area; // undefined new Circle(2).describe();// "r=2" ``` ## Why an instance cannot see a static Reading a property off `new Circle(2)` walks the object's prototype chain: the instance itself, then `Circle.prototype`, then `Object.prototype`, then `null`. The constructor `Circle` is not a link in that chain — it only *holds* `Circle.prototype` in its `.prototype` property. Nothing about that arrangement lets an instance fall back to the class. That is the whole explanation for `instance.area === undefined`, and it is why you must write `Circle.area(...)`, or `this.constructor.area(...)` if you are inside an instance method and want to reach the static through the object's own class. ## Only one copy exists A static field is one property on one object. It is not copied per instance, and mutating it is visible to every reader of the class for the lifetime of the program: ```js class Widget { static made = 0; constructor() { Widget.made++; } } new Widget(); new Widget(); Widget.made; // 2 ``` That sharing is exactly why static counters and caches need care: any code holding the class can read or write them. ## Property attributes Static methods are defined the same way prototype methods are — writable, configurable, **non-enumerable**. Public static fields are created as ordinary data properties: writable, configurable, and **enumerable**. The observable consequence: ```js Object.keys(Circle); // ["PI"] — the field, not the method Object.getOwnPropertyNames(Circle); // includes "length", "name", "prototype", "PI", "area" ``` So a `for...in` or `Object.keys` over a class shows static fields but hides static methods. Static fields also evaluate top-to-bottom at class-definition time, and their initializer expressions run with `this` bound to the class, which lets a later field read an earlier one: `static DIAMETER_FACTOR = 2; static CIRC_FACTOR = this.DIAMETER_FACTOR * this.PI;`. ## `this` inside a static Inside a static method, `this` is the object the method was called on — normally the class. `Circle.area(2)` gives `this === Circle`. Because class bodies are always strict-mode code, detaching the method loses that binding rather than falling back to a global: ```js const area = Circle.area; area(2); // TypeError: Cannot read properties of undefined (reading 'PI') ``` If you need a free-floating reference, bind it or wrap it: `const area = r => Circle.area(r)`. ## What statics are actually for The standard library is the best catalogue of good uses. Named constructors and factories that return an instance without the caller writing `new` (`Promise.resolve`, `Array.from`, `Object.create`); operations that belong to the type rather than to any one value (`Number.isNaN`, `JSON.parse`); shared constants (`Number.MAX_SAFE_INTEGER`). Application code follows the same shapes: `static fromJSON(raw)` that validates and returns an instance, `static EMPTY` as a shared sentinel, a lookup table computed once. ## The bugs this causes The two everyday mistakes are writing `this.PI` inside an *instance* method and getting `undefined` (instances do not inherit statics — use `Circle.PI` or `this.constructor.PI`), and calling `instance.someStatic()` and getting a `TypeError: instance.someStatic is not a function`. Both are the same prototype-chain fact seen from different sides.

  • You are inside an instance method and need a static constant from the same class. What do you write?
    Either the class name (`Circle.PI`) or `this.constructor.PI`. The second follows the actual class of the object, so a subclass that redefines the constant is respected; the first always reads the class you named, which is what you want when the value must not vary. Plain `this.PI` reads an instance property and is almost always `undefined`.
  • Which static members show up in Object.keys of a class, and which do not?
    Public static fields are enumerable own properties, so they appear. Static methods are defined non-enumerable, exactly like prototype methods, so they do not — you need `Object.getOwnPropertyNames` to see them. Private static members (`static #x`) never appear in any reflection API at all.
  • Can a static method reach per-instance state?
    Only through an instance passed to it. `this` in a static is the class, so there is no receiver holding instance fields. That is why statics that need object state take it as a parameter — `static compare(a, b)` — instead of pretending to have an implicit one.

saying these in an interview costs you the question

  • Says instances inherit statics through the prototype chain
  • Claims each instance gets its own copy of a static field
  • Thinks instance.staticMethod() works
  • Confuses static with private or with read-only
  • Uses this.CONSTANT in an instance method for a static field

context

open as a page

Given `class Base { static create() { return new this(); } }` and `class Sub extends Base {}`, what does `Sub.create()` return, and why does `Sub` have a `create` method at all?

level: middleimportance: should knowfreq 50%

basics

~20 s

Sub.create() returns a Sub instance. Sub inherits create because extends makes Base the prototype of the Sub constructor object, so static lookups fall through, and this inside a static is the class it was called on — here Sub.

open as a page

What is a `static { ... }` initialization block in a JavaScript class body — when does it run, and what is `this` inside it?

level: middleimportance: should knowfreq 34%

basics

~20 s

A static initialization block is a chunk of statements that runs once while the class is being defined, in source order with the static field initializers. Inside it, this is the class constructor, and it can read the class's private names.

open as a page

A class has `static #count = 0` and `static bump() { this.#count++; }`. Calling `Base.bump()` works, but `Sub.bump()` on `class Sub extends Base {}` throws a TypeError. Why, and how do you fix it?

level: seniorimportance: nice to knowfreq 20%

basics

~20 s

A private static member is installed only on the class that declares it. Inside the inherited method this is Sub, which carries no #count slot, so the private access fails its brand check and throws a TypeError. Reference the declaring class by name instead.

open as a page