Suggest an editImprove this articleRefine the answer for “Static methods in JavaScript”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Static methods in JavaScript** are functions attached to the class constructor, not to instances or the prototype. ```javascript class MathUtils { static sum(a, b) { return a + b; } } MathUtils.sum(3, 4); // 7 new MathUtils().sum(); // TypeError ``` **Key point:** use static when the logic needs no instance state.Shown above the full answer for quick recall.Answer (EN)Image**Static methods in JavaScript** are functions attached directly to the class constructor, not to instances or the prototype. You call them with the class name, not with `new`. ## Theory ### TL;DR - Analogy: like `Math.max()` - you call `Math`, not an instance of Math - `ClassName.method()` works; `instance.method()` throws a TypeError - Static methods live on the class constructor, skipping the prototype chain entirely - Use when the logic needs no `this` from an instance - Decision rule: no instance state needed? Make it static ### Quick example ```javascript class Calculator { static add(a, b) { // Attached to Calculator, not to prototype return a + b; } } console.log(Calculator.add(3, 4)); // 7 const calc = new Calculator(); calc.add(3, 4); // TypeError: calc.add is not a function ``` The `add` method never touches instance data, so it belongs on the class. The instance lookup fails because static methods never reach the prototype. ### Key difference Instance methods are stored on `Class.prototype` and available to every object created with `new`. Static methods are stored directly on the class constructor function. So `calc.__proto__` has no `add` property - that is why the TypeError happens. This is not a quirk; it is the spec working exactly as designed. ### When to use - Math or string utilities with no state: `MathUtils.sum(a, b)`, `StringUtils.slugify(str)` - Factory methods that build instances from raw data: `Person.fromString('Alice, 30')` - App-wide helpers you want grouped under a class name, not scattered as loose functions - Validators reused across multiple routes, like `RouteValidator.validateUserId(id)` in an Express app ### How it works internally V8 stores static methods as regular properties on the constructor function during class evaluation, not on `Class.prototype`. When you call `MyClass.staticMethod()`, the engine does a direct property lookup on the constructor and binds `this` to the class itself - no prototype chain walk, no instance allocation. ### Common mistakes **Calling a static via an instance** ```javascript const utils = new MathUtils(); utils.sum(1, 2); // TypeError: utils.sum is not a function ``` Statics are on the class, not on `Class.prototype`. The instance has no path to them. Fix: `MathUtils.sum(1, 2)`. **Expecting `this` to be the instance** ```javascript class Example { static whoami() { console.log(this); } } Example.whoami(); // Logs the Example class, not undefined ``` Inside a static method, `this` is the class constructor - not an instance and not `undefined`. Developers coming from Java or Python sometimes expect `null` here. It is not. Avoid relying on `this` in statics; pass data as parameters instead. **Mutable shared state via static properties** ```javascript class Counter { static count = 0; static increment() { this.count++; } } Counter.increment(); Counter.increment(); // Counter.count is now 2, globally ``` I have seen this pattern cause phantom test failures in CI pipelines where tests share class state without realizing it. Every call mutates the same `count`. For isolated state, use instance properties or a closure. **Inheritance without `super`** ```javascript class Animal { static getSpecies() { return 'Animal'; } } class Dog extends Animal {} console.log(Dog.getSpecies()); // 'Animal', not 'Dog' ``` The static is inherited from `Animal`, so `Dog` gets the parent version. If the subclass needs its own label, override it or chain with `super`: ```javascript class Bird extends Animal { static getSpecies() { return super.getSpecies() + ' (Bird)'; } } console.log(Bird.getSpecies()); // 'Animal (Bird)' ``` ### Real-world usage - `Math.max()`, `Math.min()`, `Array.isArray()`, `Promise.all()` - the standard library is built on statics - `path.join()` in Node.js - pure utility, no instance needed - `express.json()` - static-style middleware factory - `Date.now()` - returns a timestamp without creating a Date object - Lodash utilities like `_.debounce()` follow the same stateless-utility pattern ### Follow-up questions **Q:** How do static methods differ from prototype methods internally? **A:** Prototype methods are stored on `Class.prototype` and reached via the prototype chain when you call `instance.method()`. Static methods are properties on the class constructor itself. No chain walk for statics. **Q:** Can a static method access instance properties? **A:** Not directly. `this` inside a static is the class, not an instance. If you need instance data, pass the object as an argument: `MyClass.process(someInstance)`. **Q:** What is `this` inside a static method? **A:** The class constructor function. So `MyClass.someStatic()` binds `this` to `MyClass`, not to any object created with `new`. **Q:** When would you use a plain function instead of a static method? **A:** When the helper has no real connection to a specific class. Statics are for grouping under a namespace. A free function is simpler when that grouping adds no clarity. **Q:** (Senior) Explain static inheritance and the `super` keyword in statics. **A:** Subclasses inherit parent static methods through the prototype chain on the constructor level: `Dog.__proto__ === Animal` is true, so `Dog.getSpecies()` finds `Animal.getSpecies`. Inside an override you can call `super.getSpecies()` to reach the parent static explicitly. ## Examples ### Factory method: creating an instance from a string ```javascript class Person { constructor(name, age) { this.name = name; this.age = age; } static fromString(str) { const [name, age] = str.split(', '); return new Person(name, parseInt(age, 10)); } } const alice = Person.fromString('Alice, 30'); console.log(alice.name); // 'Alice' console.log(alice.age); // 30 ``` `fromString` creates a `Person` without exposing the constructor signature to the caller. The creation logic stays in one place, and you can add more factories like `Person.fromJSON(json)` later without changing call sites. ### Input validator in an Express route ```javascript class RouteValidator { static validateUserId(id) { const parsed = Number(id); if (!Number.isInteger(parsed) || parsed <= 0) { throw new Error('Invalid user ID'); } return parsed; } } app.get('/users/:id', (req, res) => { try { const userId = RouteValidator.validateUserId(req.params.id); res.json({ userId }); } catch (err) { res.status(400).json({ error: err.message }); } }); // GET /users/123 -> { userId: 123 } // GET /users/abc -> 400 { error: 'Invalid user ID' } ``` No instance of `RouteValidator` is ever created. The validation logic is grouped under a readable name and reusable across every route that needs it. This is the most common real-world pattern for static methods in Node.js backends.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.