Revealing Module Pattern

Revealing Module Pattern — вариант Module Pattern, при котором весь код (приватный и публичный) объявляется в одной области видимости IIFE, а в return возвращаются ссылки только на публичные функции.

Зачем нужно

Паттерн решает проблему классического Module Pattern, где публичные и приватные функции вперемешку. В Revealing Module все функции объявляются одинаково, а публичный API явно декларируется в одном месте — в return. Это упрощает чтение кода и понимание контракта модуля.

Где используется

  • Библиотеки и утилиты без ES-модулей
  • Legacy-код на jQuery и ванильном JS
  • Изоляция кода в браузерных расширениях
  • Паттерн всё ещё применяется в обучении и понимании замыканий

Основной контент

Базовый Revealing Module

const Counter = (function {
  // Все переменные и функции — приватные по умолчанию
  let count = 0;
  const step = 1;

  function increment() {
    count += step;
  }

  function decrement() {
    if (count > 0) count -= step;
  }

  function reset() {
    count = 0;
  }

  function getCount() {
    return count;
  }

  // Явно раскрываем только публичный API
  return {
    increment,
    decrement,
    reset,
    get: getCount // переименование при раскрытии
  };
});

Counter.increment;
Counter.increment;
Counter.increment;
console.log(Counter.get); // 3
Counter.decrement;
console.log(Counter.get); // 2

// count и step недоступны извне
console.log(Counter.count); // undefined

Сравнение с обычным Module Pattern

// Обычный Module Pattern — публичные методы определяются в return
const ModuleA = (function {
  let _private = 0;

  return {
    // Публичный метод определён прямо здесь
    increment { _private++; },
    get { return _private; }
  };
});

// Revealing Module — все методы определены вверху
const ModuleB = (function {
  let _private = 0;

  // Всё определено одинаково
  function increment() { _private++; }
  function get() { return _private; }
  function _helperPrivate() { /* ... */ }

  // Только здесь видно что публично
  return { increment, get };
});

Revealing Module с фабрикой (несколько экземпляров)

function createShoppingCart() {
  const items = ;

  function add(item) {
    const existing = items.find(i => i.id === item.id);
    if (existing) {
      existing.qty++;
    } else {
      items.push({ ...item, qty: 1 });
    }
  }

  function remove(id) {
    const index = items.findIndex(i => i.id === id);
    if (index !== -1) items.splice(index, 1);
  }

  function total() {
    return items.reduce((sum, i) => sum + i.price * i.qty, 0);
  }

  function getItems() {
    return [...items]; // возвращаем копию
  }

  function clear() {
    items.length = 0;
  }

  return { add, remove, total, getItems, clear };
}

const cart1 = createShoppingCart;
const cart2 = createShoppingCart; // независимый экземпляр

cart1.add({ id: 1, name: 'Книга', price: 500 });
console.log(cart1.total); // 500
console.log(cart2.total); // 0

Частые ошибки

  • Переопределение публичных свойств через возвращённый объект — если Counter.increment = something, внутренняя ссылка не меняется. Другие методы модуля всё равно вызовут оригинальную функцию, что вводит в заблуждение.
  • Применение в проектах с ES-модулями — в современных проектах с bundler используйте нативные export/import. Revealing Module — решение для окружений без систем модулей.
  • Нет поддержки наследования — паттерн не предназначен для иерархий. Для OOP используйте классы.

Связанные темы

Ресурсы