87. Модульный паттерн (IIFE)
Модульный паттерн позволяет создавать инкапсулированные модули с приватными переменными и публичным API — ещё до появления class и ES Modules. Основан на IIFE (Immediately Invoked Function Expression) и замыканиях.
IIFE — основа паттерна
Заголовок раздела «IIFE — основа паттерна»IIFE (Immediately Invoked Function Expression) — функция, которая вызывается сразу после объявления:
// Классический IIFE;(function() { const secret = 'приватная переменная' console.log(secret) // доступна внутри})()
// console.log(secret) // ReferenceError — снаружи недоступна
// Стрелочный IIFE;(() => { const local = 42 console.log(local)})()IIFE создаёт изолированную область видимости — переменные внутри не загрязняют глобальное пространство.
Базовый модульный паттерн
Заголовок раздела «Базовый модульный паттерн»const counter = (function() { // Приватное состояние let count = 0
// Публичный API (возвращаемый объект) return { increment() { count++ }, decrement() { count-- }, reset() { count = 0 }, getCount() { return count }, }})()
counter.increment()counter.increment()counter.increment()counter.decrement()
console.log(counter.getCount()) // 2console.log(counter.count) // undefined — приватная!Revealing Module Pattern
Заголовок раздела «Revealing Module Pattern»«Раскрывающий» модульный паттерн: всё определяется приватно, а в конце явно «раскрываются» нужные функции:
const userModule = (function() { // Приватные переменные let users = [] let nextId = 1
// Приватные функции function findById(id) { return users.find(u => u.id === id) }
function validate(name, email) { return name.length >= 2 && email.includes('@') }
// Публичные функции (те же функции, но раскрытые) function addUser(name, email) { if (!validate(name, email)) throw new Error('Некорректные данные') const user = { id: nextId++, name, email } users.push(user) return user }
function removeUser(id) { const index = users.findIndex(u => u.id === id) if (index === -1) throw new Error('Пользователь не найден') return users.splice(index, 1)[0] }
function getUser(id) { return findById(id) ? { ...findById(id) } : null // возвращаем копию }
function getAll() { return [...users] // возвращаем копию массива }
// Revealing: явно раскрываем только нужное return { addUser, removeUser, getUser, getAll }})()
console.log(userModule.getAll()) // оба пользователяconsole.log(userModule.getUser(1)) // { id: 1, name: 'Алексей', ... }// userModule.validate // undefined — скрыта// userModule.findById // undefined — скрытаПаттерн с параметрами
Заголовок раздела «Паттерн с параметрами»Модуль с настройками:
function createLogger(prefix = '[LOG]', { timestamps = true } = {}) { // Приватное состояние const logs = []
function formatMessage(level, msg) { const time = timestamps ? `[${new Date().toISOString()}]` : '' return `${prefix} ${time} [${level}] ${msg}` }
return { log(msg) { const m = formatMessage('INFO', msg); logs.push(m); console.log(m) }, warn(msg) { const m = formatMessage('WARN', msg); logs.push(m); console.warn(m) }, error(msg) { const m = formatMessage('ERROR', msg); logs.push(m); console.error(m) }, getHistory() { return [...logs] }, clear() { logs.length = 0 }, }}
const logger = createLogger('[APP]', { timestamps: false })logger.log('Приложение запущено')logger.warn('Осторожно!')logger.error('Ошибка подключения')console.log('История:', logger.getHistory().length, 'записей')Namespace Pattern
Заголовок раздела «Namespace Pattern»Группировка функций в пространстве имён:
const MyApp = MyApp || {}
MyApp.utils = (function() { function formatDate(date) { return new Intl.DateTimeFormat('ru-RU').format(date) }
function truncate(str, len = 50) { return str.length <= len ? str : str.slice(0, len) + '...' }
return { formatDate, truncate }})()
MyApp.api = (function() { const BASE_URL = 'https://api.example.com'
async function get(endpoint) { const response = await fetch(`${BASE_URL}${endpoint}`) return response.json() }
return { get }})()
console.log(MyApp.utils.formatDate(new Date()))console.log(MyApp.utils.truncate('Очень длинная строка которая будет обрезана', 20))Сравнение: Module Pattern vs ES Modules
Заголовок раздела «Сравнение: Module Pattern vs ES Modules»| Критерий | Module Pattern | ES Modules |
|---|---|---|
| Поддержка браузеров | Везде | Современные браузеры |
| Приватность | Через замыкание | Только в модуле |
| Синтаксис | Сложнее | Чище, стандартный |
| Tree shaking | Нет | Да |
| Когда использовать | Legacy, нет сборщика | Современные проекты |
Задания для практики
Заголовок раздела «Задания для практики»- Реализуйте
EventEmitterкак Revealing Module Pattern с методамиon,off,emit. - Создайте модуль
shoppingCartс приватным массивом товаров и публичными методамиadd,remove,total. - Напишите IIFE-обёртку для работы с
localStorage, которая автоматически сериализует/десериализует JSON.