Статические поля в классах JavaScript представляют собой свойства, принадлежащие самому классу, а не его экземплярам. В SWC (Speedy Web Compiler) поддержка статических полей реализуется через трансформацию синтаксиса в совместимый ES5/ES2015 код, что особенно важно при работе в средах без нативной поддержки class fields.
Базовое объявление статического поля:
class User {
static role = "guest";
}
После трансформации SWC подобный код может быть преобразован в форму, эквивалентную присваиванию свойства классу:
class User {}
User.role = "guest";
SWC обрабатывает статические поля на этапе компиляции, перемещая их из тела класса в присваивания после определения конструктора. Это обеспечивает:
При наличии нескольких статических полей порядок их инициализации сохраняется:
class Config {
static host = "localhost";
static port = 8080;
}
Трансформация:
class Config {}
Config.host = "localhost";
Config.port = 8080;
SWC поддерживает вычисляемые выражения:
class MathConfig {
static base = 10;
static doubleBase = this.base * 2;
}
После трансформации доступ к this в статическом контексте
корректируется на имя класса:
class MathConfig {}
MathConfig.base = 10;
MathConfig.doubleBase = MathConfig.base * 2;
Такое поведение критично для сохранения семантики, поскольку внутри
статической инициализации this указывает на сам класс.
Статические методы принадлежат классу и не требуют создания экземпляра для вызова. SWC транслирует их в обычные свойства функции-конструктора.
Исходный код:
class Logger {
static log(message) {
console.log(message);
}
}
После компиляции:
class Logger {}
Logger.log = function (message) {
console.log(message);
};
Внутри статических методов this указывает на сам класс:
class Service {
static name = "API";
static getName() {
return this.name;
}
}
SWC сохраняет это поведение, заменяя контекст на явную ссылку:
class Service {}
Service.name = "API";
Service.getName = function () {
return Service.name;
};
Статические методы часто используют статические поля для хранения конфигурации или состояния класса:
class App {
static version = "1.0";
static getVersion() {
return this.version;
}
static bumpVersion() {
const [major, minor] = this.version.split(".");
this.version = `${major}.${Number(minor) + 1}`;
}
}
SWC преобразует это в последовательное присваивание:
class App {}
App.version = "1.0";
App.getVersion = function () {
return App.version;
};
App.bumpVersion = function () {
const [major, minor] = App.version.split(".");
App.version = `${major}.${Number(minor) + 1}`;
};
SWC поддерживает приватные статические поля через трансформацию с использованием WeakMap или замыканий (в зависимости от конфигурации).
Исходный код:
class Counter {
static
static increment() {
this.#value++;
}
static getValue() {
return this.#value;
}
}
Типичная трансформация SWC:
var _value = new WeakMap();
class Counter {}
_value.set(Counter, 0);
Counter.increment = function () {
_value.set(Counter, _value.get(Counter) + 1);
};
Counter.getValue = function () {
return _value.get(Counter);
};
Ключевые аспекты:
Хотя основная цель SWC — поддержка статических полей и методов, статические блоки инициализации также влияют на порядок выполнения:
class Database {
static connection;
static {
this.connection = createConnection();
}
}
SWC разворачивает статический блок в последовательный код инициализации:
class Database {}
Database.connection = undefined;
Database.connection = createConnection();
Если в классе присутствуют и поля, и статические блоки, SWC строго сохраняет порядок их объявления.
Статические поля и методы наследуются, что SWC учитывает при генерации кода:
class Base {
static type = "base";
static getType() {
return this.type;
}
}
class Derived extends Base {}
В результате трансформации:
class Base {}
Base.type = "base";
Base.getType = function () {
return Base.type;
};
class Derived extends Base {}
При этом доступ через наследника сохраняется благодаря прототипной цепочке:
Derived.getType(); // "base"
SWC не изменяет семантику переопределения, но фиксирует вызовы через конкретный идентификатор класса:
class A {
static run() {
return "A";
}
}
class B extends A {
static run() {
return "B";
}
}
Трансформация:
class A {}
A.run = function () {
return "A";
};
class B extends A {}
B.run = function () {
return "B";
};
Вызовы остаются строго привязанными к конкретному классу, без
динамического разрешения имени через super в статическом
контексте.
При работе со статическими полями и методами существуют особенности:
this в статике без явной подстановки имени класса;
loose и
strict трансформации;
В режиме loose SWC упрощает трансформации:
class A {
static x = 1;
}
Может быть преобразовано максимально прямо:
class A {}
A.x = 1;
В более строгом режиме добавляются вспомогательные конструкции для точного соблюдения спецификации.
SWC поддерживает приватные статические методы аналогично полям:
class Auth {
static #hash(password) {
return password + "_hashed";
}
static encode(password) {
return this.#hash(password);
}
}
Трансформация:
var _hash = new WeakSet();
class Auth {}
_hash.add(Auth);
Auth.encode = function (password) {
return password + "_hashed";
};
При этом внутренняя логика может различаться в зависимости от версии SWC и включённых трансформеров, но принцип изоляции остаётся неизменным.
this внутри статических методов переписывается на
явную ссылку класса;