Парадигмы
Парадигма программирования — стиль и подход к написанию кода. JavaScript — мультипарадигменный язык: поддерживает императивный, объектно-ориентированный и функциональный стили.
Зачем нужно
Понимание парадигм помогает выбрать правильный подход для конкретной задачи. Чистый функциональный стиль упрощает тестирование, ООП удобен для моделирования предметной области, а императивный — для пошаговых алгоритмов.
Где используется
Везде. React тяготеет к функциональному стилю, Angular — к ООП, Node.js middleware — к функциональным паттернам. Знание парадигм позволяет осознанно выбирать стиль.
Предпосылки
Императивный стиль
Описывает как делать — пошаговые инструкции:
// Найти сумму чётных чисел
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let sum = 0;
for (let i = 0; i < numbers.length; i++) {
if (numbers[i] % 2 === 0) {
sum += numbers[i];
}
}
console.log(sum); // 30
Декларативный стиль
Описывает что нужно получить:
const sum = numbers
.filter(n => n % 2 === 0)
.reduce((acc, n) => acc + n, 0);
console.log(sum); // 30
Объектно-ориентированный стиль
Данные и поведение объединены в объекты:
class ShoppingCart {
#items = ;
addItem(item) {
this.#items.push(item);
return this;
}
removeItem(id) {
this.#items = this.#items.filter(item => item.id !== id);
return this;
}
get total {
return this.#items.reduce((sum, item) => sum + item.price * item.qty, 0);
}
get itemCount {
return this.#items.reduce((sum, item) => sum + item.qty, 0);
}
}
const cart = new ShoppingCart();
cart.addItem({ id: 1, name: 'Книга', price: 500, qty: 2 })
.addItem({ id: 2, name: 'Ручка', price: 50, qty: 5 });
console.log(cart.total); // 1250
console.log(cart.itemCount); // 7
Функциональный стиль
Данные и функции разделены, состояние не мутируется:
// Данные — простые структуры
const cart = { items: };
// Функции — чистые, без побочных эффектов
const addItem = (cart, item) => ({
...cart,
items: [...cart.items, item]
});
const removeItem = (cart, id) => ({
...cart,
items: cart.items.filter(item => item.id !== id)
});
const getTotal = (cart) =>
cart.items.reduce((sum, item) => sum + item.price * item.qty, 0);
// Использование — каждая операция возвращает новый объект
const cart1 = addItem(cart, { id: 1, name: 'Книга', price: 500, qty: 2 });
const cart2 = addItem(cart1, { id: 2, name: 'Ручка', price: 50, qty: 5 });
const cart3 = removeItem(cart2, 1);
console.log(getTotal(cart2)); // 1250
console.log(getTotal(cart3)); // 250
console.log(cart.items); // — оригинал не изменился
Сравнение парадигм
| Аспект | Императивный | ООП | Функциональный |
|---|---|---|---|
| Фокус | Как делать | Объекты и сообщения | Преобразование данных |
| Состояние | Мутируемое | Инкапсулированное | Иммутабельное |
| Побочные эффекты | Везде | Контролируемые | Минимизированы |
| Переиспользование | Процедуры | Наследование | Композиция |
| Тестирование | Сложнее | Средне | Проще |
| Типичный пример | Алгоритмы | Модели, UI | Обработка данных |
JavaScript — мультипарадигменный
// Одна задача — три стиля
// Императивный
function getActiveUsersImperative(users) {
const result = ;
for (const user of users) {
if (user.active) {
result.push(user.name.toUpperCase());
}
}
result.sort();
return result;
}
// ООП
class UserCollection {
constructor(users) { this.users = users; }
active {
return new UserCollection(this.users.filter(u => u.active));
}
names { return this.users.map(u => u.name.toUpperCase()); }
sorted {
return new UserCollection(
[...this.users].sort((a, b) => a.name.localeCompare(b.name))
);
}
toArray { return this.users; }
}
// Функциональный
const getActiveUsersFP = (users) =>
users
.filter(u => u.active)
.map(u => u.name.toUpperCase())
.sort();
Когда какой стиль
| Задача | Рекомендуемый стиль |
|---|---|
| Обработка коллекций данных | Функциональный |
| Сложная бизнес-логика с состоянием | ООП |
| Низкоуровневые алгоритмы | Императивный |
| React-компоненты | Функциональный |
| Node.js middleware | Функциональный |
| Игровые объекты | ООП |
| Утилиты и хелперы | Функциональный |
| DOM-манипуляции | Императивный/ООП |
Частые ошибки
1. Догматичный выбор одной парадигмы
// Не нужно всё делать функционально:
// Иногда for-цикл понятнее цепочки map/filter/reduce
// Не нужно всё оборачивать в классы:
// Простая функция лучше класса с одним методом
2. Смешение стилей без причины
// Плохо — мутация в "функциональной" цепочке
const result = data
.filter(x => { sideEffect; return x > 0; }) // побочный эффект!
.map(x => x * 2);
// Хорошо — чистые функции в цепочке
const result = data
.filter(x => x > 0)
.map(x => x * 2);
Практика
- Реши одну задачу (найти максимальный элемент) в трёх стилях
- Перепиши императивный код с циклами на функциональный с map/filter/reduce
- Создай простую модель данных в ООП и ФП стилях — сравни
- Определи, какой стиль используется в 5 фрагментах кода
- Напиши утилиту обработки данных в функциональном стиле с pipe/compose