Приватные поля вида ECMAScript и обеспечивают инкапсуляцию на
уровне языка. В отличие от соглашений через _underscore,
они имеют жёсткую семантическую защиту: доступ вне тела класса приводит
к ошибке выполнения.
class User {
#token = "secret";
getToken() {
return this.#token;
}
}
const u = new User();
u.getToken(); // "secret"
u.#token; // SyntaxError
На уровне транспиляции такие конструкции требуют специальной обработки, поскольку старые версии JavaScript-движков не поддерживают приватные поля. Именно здесь поведение компилятора становится критическим: необходимо не только сохранить семантику доступа, но и обеспечить безопасность и производительность.
SWC SWC реализует трансформацию приватных полей через несколько стратегий, выбираемых в зависимости от конфигурации и целевого окружения.
Приватные поля обладают рядом свойств, которые усложняют их преобразование:
obj[“#field”]
недопустимо)
#field и публичными свойствами с тем же
именем
Следовательно, трансформация должна имитировать:
SWC использует несколько подходов в зависимости от настроек:
Наиболее близкая к спецификации реализация строится через
WeakMap.
const _token = new WeakMap();
class User {
constructor() {
_token.set(this, "secret");
}
getToken() {
return _token.get(this);
}
}
Альтернативный подход — замыкания внутри фабричной функции класса.
var User = (function () {
var _token = new WeakMap();
function User() {
_token.set(this, "secret");
}
User.prototype.getToken = function () {
return _token.get(this);
};
return User;
})();
Этот вариант часто используется в более старых target-окружениях или при специфических настройках транспиляции.
В некоторых конфигурациях SWC может использовать оптимизированные структуры, если:
loose режим
Пример упрощённой модели:
class User {
constructor() {
this._token = "secret";
}
getToken() {
return this._token;
}
}
Основной контроль осуществляется через .swcrc:
{
"jsc": {
"parser": {
"syntax": "ecmascript",
"privateMethod": true,
"classPrivateProperty": true
},
"transform": {
"legacyDecorator": false
},
"target": "es5"
}
}
Ключевые параметры:
classPrivateProperty — включает поддержку трансформации
#field
target — определяет уровень JavaScript-выхода
loose — влияет на стратегию эмуляции приватности
При компиляции SWC выполняет несколько стадий:
Исходный код:
class Counter {
#value = 0;
inc() {
this.#value++;
}
}
Преобразуется в AST с узлами типа:
SWC извлекает приватные поля в область модуля:
var _value = new WeakMap();
или:
var _value = new WeakSet(); // для методов
class Counter {
constructor() {
_value.set(this, 0);
}
}
Если конструктор отсутствует, SWC генерирует его автоматически.
Операции:
this.#value
заменяется на:
_value.get(this)
this.#value = 1
становится:
_value.set(this, 1)
this.#value++
разворачивается в:
_value.set(this, _value.get(this) + 1)
Приватные поля не наследуются. SWC строго сохраняет это поведение.
class A { #x = 1; }
class B extends A { getX() { return this.#x; // ошибка на этапе
трансформации/выполнения } }
После трансформации SWC создаёт отдельные WeakMap для каждого класса:
var _xA = new WeakMap();
class A {
constructor() {
_xA.set(this, 1);
}
}
Класс B не имеет доступа к _xA.
Статические поля преобразуются отдельно, так как они принадлежат классу, а не экземпляру.
class Service {
static #cache = new Map();
}
Трансформация:
var _cache = new WeakMap();
class Service {
static getCache() {
return _cache.get(Service);
}
}
Особенность: вместо this используется объект класса.
SWC может применять кеширование доступа при частых обращениях:
const _value = new WeakMap();
function get(obj) {
return _value.get(obj);
}
и далее:
const v = get(this);
Это уменьшает количество вызовов .get/.set, особенно в
циклах.
Хотя SWC и Babel решают одинаковую задачу, их подход отличается:
В трансформации приватных полей различия проявляются в:
| Характеристика | SWC | Babel |
|---|---|---|
| Стратегия | WeakMap / оптимизации | WeakMap / helper runtime |
| Скорость | выше | ниже |
| Настройка | ограниченная | гибкая |
| Плагины | минимальные | широкие |
const { method } = instance;
method();
Если метод использует #private, возможна потеря контекста.
SWC не всегда может предупредить об этом.
const p = new Proxy(obj, {});
p.#field;
Приватные поля не перехватываются Proxy, и SWC сохраняет это поведение.
JSON.stringify(instance)
Приватные поля не участвуют в сериализации, поскольку хранятся в WeakMap или замыканиях.
Использование приватных полей через WeakMap увеличивает:
Однако:
Обобщённо SWC превращает приватные поля в одну из форм:
WeakMap(this → value) — основная модель
closure state — функциональная модель
inline property — оптимизированная, но небезопасная модель
Каждый вариант выбирается автоматически на основе:
loose
Результатом становится код, который сохраняет поведение ECMAScript-приватности даже в средах без её нативной поддержки, при этом балансируя между корректностью и производительностью.