Статические члены принадлежат классу, а не экземпляру
Большинство методов, которые вы пишете в классе, работают с конкретным экземпляром — с конкретным user, с конкретным circle. Со статическими членами всё иначе: они принадлежат самому классу. Вызываются такие методы через имя класса, а не через экземпляр.
double живёт на самом MathUtils, а не на его экземплярах. Создавать объект бесполезно — m.double вернёт undefined. С обычными методами всё ровно наоборот: они принадлежат экземплярам (точнее, прототипу) и недоступны через саму ссылку на класс.
Удобная ментальная модель: ключевое слово class создаёт сразу две «корзины» — методы экземпляра (их использует new MathUtils()) и статические методы класса (их вызывают прямо на MathUtils). Ключевое слово static как раз и решает, в какую корзину попадёт член класса.
Фабричные методы — классический сценарий
Чаще всего статические методы пишут ради альтернативных конструкторов. Обычный constructor принимает свой фиксированный набор аргументов, но на практике объект нужно собирать по-разному — из JSON, из строки в базе, из URL.
fromJSON не использует пользователя — он его создаёт. Как раз для таких случаев и нужны статические методы класса. Можно было бы, конечно, завести отдельную функцию parseUser где-то рядом, но держать её прямо на классе удобнее: связанное поведение лежит в одном месте, а в месте вызова сразу понятно, о чём речь.
Статические свойства класса
К самому классу можно прикреплять не только методы, но и данные:
Circle.PI — это общая константа для всех кругов: она живёт на самом классе и не копируется в каждый экземпляр. Внутри обычных методов обращаться к ней нужно через имя класса (Circle.PI), а не через this.
Статические свойства удобны для конфигурации, общих для всех экземпляров кэшей, счётчиков и констант уровня класса.
Что такое this в статическом методе JavaScript
В обычном методе this указывает на экземпляр, а вот в статическом методе this ссылается на сам класс:
this.count внутри increment — это то же самое, что Counter.count. Поначалу это может показаться странным, но именно благодаря этому работает наследование статических методов: this ссылается на тот класс, у которого вызвали метод, а не на тот, в котором его определили.
Наследование статических методов в JavaScript
Статические методы наследуются подклассами. А поскольку this указывает именно на класс, у которого метод был вызван, фабричные методы автоматически создают экземпляры нужного подкласса:
Animal.create внутри использует new this(name). Когда вы вызываете Dog.create("Rex"), this указывает на Dog, поэтому new this(name) создаёт именно экземпляр Dog. Если бы там стояло new Animal(name), результатом всегда был бы Animal — и весь паттерн бы сломался. Собственно, это и есть главная причина, почему this в статическом методе указывает на сам класс.
Статические и обычные методы: сравнение на практике
Вот как одна и та же логика выглядит в двух вариантах — через статический метод и через метод экземпляра:
Оба варианта выполняют одни и те же вычисления. Метод экземпляра берёт данные из this.celsius, а статический метод получает их через аргумент. Используйте метод экземпляра, когда операция — это «что-то, что делает сам объект»; выбирайте статический метод, когда это «что-то, что класс умеет вычислять по входным данным».
Статические блоки для инициализации
Иногда для инициализации статического свойства одного выражения мало — нужен цикл, условие или несколько связанных между собой значений. Именно для таких случаев в классах JavaScript придуманы статические блоки (static блоки):
Блок static { ... } выполняется один раз — в момент объявления класса. Внутри него this указывает на сам класс. Такой блок пригодится для многошаговой инициализации; если же нужно просто присвоить одно значение — обычное статическое поле будет нагляднее.
Приватные статические свойства и методы
Статические поля тоже можно сделать приватными — достаточно поставить префикс #. Обратиться к ним получится только изнутри класса:
#nextId надёжно спрятан внутри класса. Снаружи можно вызвать IdGenerator.next(), но дотянуться до счётчика или сбросить его — уже нет. Приватным полям посвящена отдельная страница чуть дальше, но полезно знать, что связка static и # прекрасно работает.
Когда static — не лучший выбор
Статические методы класса удобно использовать как общий контейнер для вспомогательных функций, но это не повод превращать каждую утилиту в класс. Если у вас есть файл с набором независимых функций — просто экспортируйте функции, а не оборачивайте их в класс, состоящий из одних только статических методов, ради «пространства имён». Модуль решает эту задачу сам, и делает это чище.
Тянитесь к static, когда:
- Функция действительно относится к классу (фабричный метод, конвертер, валидатор для этого типа).
- Нужно состояние, общее для всех экземпляров класса.
- Подкласс может захотеть переопределить или унаследовать это поведение.
В остальных случаях обычная функция — инструмент проще и честнее.
Дальше: приватные поля
Вы уже мельком видели #nextId — это синтаксис приватных полей в JavaScript. Он работает и для обычных, и для статических членов класса, и сегодня это основной способ спрятать детали реализации внутри класса. Как раз об этом — в следующей главе.
Часто задаваемые вопросы
Что такое статический метод в JavaScript?
Статический метод принадлежит самому классу, а не его экземплярам. Объявляется через ключевое слово static и вызывается прямо у класса: MyClass.doThing(). У экземпляров такого метода нет — через this.doThing() до него не добраться, только через имя класса.
Когда использовать статический метод вместо обычного?
Тогда, когда функция логически связана с классом, но ей не нужны данные конкретного экземпляра. Типичные примеры: фабричные методы вроде User.fromJSON(...), вспомогательные утилиты по типу Math.max и константы, для которых класс выступает просто как пространство имён. Если же методу нужен this, указывающий на экземпляр, — это уже метод экземпляра.
Можно ли из статического метода обратиться к свойствам экземпляра?
Напрямую — нет. Внутри статического метода this ссылается на сам класс, а не на экземпляр, поэтому this.name вернёт статическое свойство, а не поле объекта. Если нужны данные экземпляра, передайте его параметром: static summarize(user) { return user.name; }.
Наследуются ли статические методы в JavaScript?
Да. Когда дочерний класс наследуется от родителя через extends, статические методы родителя становятся доступны и у подкласса. При этом внутри такого метода this указывает на тот класс, у которого его вызвали, — именно благодаря этому статические фабричные методы корректно работают с наследниками.