Как заморозить enum в JavaScript и не сойти с ума

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: Почему ваш enum может «потечь», и как этого избежать

Представьте, что вы работаете над большим проектом на JavaScript. Вы создали enum для статусов заказа: PENDING, SHIPPED, DELIVERED. Всё работает идеально, пока в одном из модулей какой-то разработчик (или вы сами через полгода) случайно не пишет: OrderStatus.PENDING = 'CANCELLED'. Или, что ещё хуже, Object.assign(OrderStatus, { NEW_STATUS: 'hacked' }). Бах! Логика приложения ломается, баг уходит в прод, и вы проводите бессонную ночь за дебагом.

Это классическая проблема, которая собрала тысячи просмотров на Stack Overflow. Вопрос «How can I guarantee that my enums definition doesn't change in JavaScript?» мучает многих. И не зря: JavaScript — динамичный язык, и его объекты по умолчанию мутабельны. Но выход есть, и сегодня мы разберём все способы надёжной защиты ваших перечислений.

В этой статье я покажу вам несколько проверенных подходов: от простого Object.freeze() до продвинутых техник с Symbol и TypeScript. Вы узнаете, как писать неизменяемые enum, которые выдержат любые случайные (и не очень) изменения.

Проблема: почему enum в JavaScript — это просто объект

В отличие от некоторых языков (C#, Java, Rust), в JavaScript нет нативной конструкции enum. Разработчики эмулируют их с помощью простых объектов:

const OrderStatus = {
  PENDING: 'pending',
  SHIPPED: 'shipped',
  DELIVERED: 'delivered'
};

Это работает, но у такой эмуляции есть фатальный недостаток: любой может изменить значение. И это не только злой умысел — часто это происходит случайно:

  • Случайное присваивание в цикле или коллбэке.
  • Иммутабельность? Забыли — и кто-то расширил объект.
  • Библиотеки, которые могут мутировать переданные объекты.

Проблема усугубляется тем, что в больших кодовых базах отследить такие изменения сложно. Именно поэтому вопрос о защите enum от изменений так популярен.

Решение №1: Object.freeze() — ваш первый щит

Самый простой и распространённый способ — использовать Object.freeze(). Этот метод делает объект неизменяемым: вы не сможете добавлять, удалять или изменять свойства.

Как это работает

const OrderStatus = Object.freeze({
  PENDING: 'pending',
  SHIPPED: 'shipped',
  DELIVERED: 'delivered'
});

// Попытка изменить — не сработает
OrderStatus.PENDING = 'CANCELLED'; // Тишина (в strict mode — ошибка)
OrderStatus.NEW = 'new';           // Не добавится
delete OrderStatus.SHIPPED;       // Не удалится

console.log(OrderStatus.PENDING); // 'pending' — значение не изменилось

Почему это полезно? Object.freeze() работает на уровне самого объекта. Это встроенный механизм, который не требует дополнительных библиотек. Он идеально подходит для защиты enum в JavaScript от случайных мутаций.

Ограничения Object.freeze()

  • Неглубокий фриз — если значения enum — объекты, они остаются мутабельными.
  • Не защищает от переназначения — если вы используете let вместо const, переменную можно перезаписать целиком.
  • Не даёт ошибок в нестрогом режиме — изменения просто игнорируются, что может скрыть баг.

Но для простых enum этого более чем достаточно. Если вам нужна дополнительная защита — читайте дальше.

Решение №2: Используйте const + Object.freeze() для полной блокировки

Комбинация const (защищает от переназначения переменной) и Object.freeze() (защищает свойства) даёт максимальную защиту для простых enum:

const Colors = Object.freeze({
  RED: '#FF0000',
  GREEN: '#00FF00',
  BLUE: '#0000FF'
});

// Никаких шансов:
// Colors = {};       // Ошибка: Assignment to constant variable
// Colors.RED = 'X';  // Не сработает (strict mode — TypeError)

Этот паттерн — золотой стандарт для неизменяемых констант перечисления в JavaScript. Он простой, понятный и эффективный.

Решение №3: Symbol — чтобы значения были уникальными

Иногда нужно не просто заморозить объект, но и гарантировать, что значения enum уникальны и не конфликтуют с другими строками. Для этого используйте Symbol:

const Direction = Object.freeze({
  UP: Symbol('up'),
  DOWN: Symbol('down'),
  LEFT: Symbol('left'),
  RIGHT: Symbol('right')
});

// Каждый Symbol уникален
console.log(Direction.UP === Direction.UP);  // true
console.log(Direction.UP === 'up');          // false — никогда не совпадёт со строкой

Такой подход особенно полезен, когда вы хотите избежать коллизий имён или случайного сравнения с обычными строками. Symbol enum — это мощный инструмент для больших проектов.

Минус: Symbol не сериализуется в JSON (при JSON.stringify он превращается в null). Если вы передаёте данные по API, придётся использовать строки.

Решение №4: Прокси-объекты для максимального контроля

Для тех, кто хочет полный контроль над поведением enum, есть Proxy. С его помощью можно перехватывать любые операции: чтение, запись, удаление, и даже выводить информативные ошибки.

function createEnum(obj) {
  return new Proxy(Object.freeze({ ...obj }), {
    set(target, prop, value) {
      throw new Error(`Нельзя изменять enum: попытка установить ${prop} = ${value}`);
    },
    deleteProperty(target, prop) {
      throw new Error(`Нельзя удалять свойства enum: ${prop}`);
    }
  });
}

const Status = createEnum({
  ACTIVE: 'active',
  INACTIVE: 'inactive'
});

// Status.ACTIVE = 'test'; // Ошибка: "Нельзя изменять enum..."

Это уже продвинутый уровень. Прокси даёт вам возможность гарантировать неизменность enum на 100% и даже логировать попытки изменений. Идеально для библиотек или кода, где безопасность критична.

Решение №5: TypeScript — правильный enum с проверкой на этапе компиляции

Если вы пишете на TypeScript (а многие современные проекты переходят на него), то получаете нативный enum с защитой на уровне типов:

enum UserRole {
  Admin = 'admin',
  Editor = 'editor',
  Viewer = 'viewer'
}

// TypeScript выдаст ошибку на этапе компиляции:
// UserRole.Admin = 'superadmin'; // Error: Cannot                

Блоги

Рекомендаций по книгам