← Назад к вопросам

Что такое функциональный подход?

1.7 Middle🔥 141 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Функциональный подход (Functional Programming)

Функциональное программирование (FP) — это парадигма программирования, которая рассматривает вычисления как вычисление математических функций и избегает изменяемого состояния и побочных эффектов.

Основные принципы функционального подхода

1. Pure Functions (Чистые функции)

Чистая функция:

  • Всегда возвращает один и тот же результат для одних и тех же входных данных
  • Не имеет побочных эффектов (не изменяет внешнее состояние)
// ❌ Нечистая функция (зависит от внешнего состояния)
let counter = 0;

function increment() {
  counter++; // Модифицирует внешнюю переменную (побочный эффект)
  return counter;
}

increment(); // 1
increment(); // 2
increment(); // 2 (результат зависит от истории вызовов)

// ✅ Чистая функция
function increment(count) {
  return count + 1; // Только вычисления, без побочных эффектов
}

increment(0); // 1 (всегда 1)
increment(0); // 1 (всегда 1 для одного входа)
increment(1); // 2 (всегда 2 для одного входа)

Примеры нечистых функций:

// ❌ Изменяет глобальное состояние
function addUser(user) {
  users.push(user); // Побочный эффект
}

// ❌ Зависит от времени
function getCurrentTime() {
  return new Date(); // Разный результат в разное время
}

// ❌ Обращается к DOM
function getInputValue() {
  return document.getElementById('input').value; // Зависит от DOM
}

// ❌ API запросы
function fetchUser(id) {
  return fetch(`/api/users/${id}`); // Побочный эффект (сетевой запрос)
}

2. Immutability (Неизменяемость)

Не модифицируй исходные данные, создавай новые объекты.

// ❌ Изменяет исходный массив
function addToList(list, item) {
  list.push(item); // Мутирует
  return list;
}

const myList = [1, 2, 3];
addToList(myList, 4);
console.log(myList); // [1, 2, 3, 4] — изменился!

// ✅ Создаёт новый массив
function addToList(list, item) {
  return [...list, item]; // Spread operator — новый массив
}

const myList = [1, 2, 3];
const newList = addToList(myList, 4);
console.log(myList); // [1, 2, 3] — не изменился
console.log(newList); // [1, 2, 3, 4] — новый массив

// ✅ Или Object.assign для объектов
const user = { name: 'John', age: 30 };
const updatedUser = { ...user, age: 31 }; // Новый объект
// Или
const updatedUser = Object.assign({}, user, { age: 31 });

3. First-Class Functions (Функции как значения)

Функции можно передавать как аргументы, возвращать из функций, сохранять в переменных.

// ✅ Функция как значение
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;

// ✅ Функция как аргумент (callback)
function calculate(a, b, operation) {
  return operation(a, b);
}

calculate(5, 3, add); // 8
calculate(5, 3, multiply); // 15

// ✅ Функция возвращает функцию (higher-order function)
function multiplier(factor) {
  return (number) => number * factor;
}

const double = multiplier(2);
const triple = multiplier(3);

double(5); // 10
triple(5); // 15

4. Higher-Order Functions (Функции высшего порядка)

Функции, которые принимают другие функции как аргументы или возвращают функции.

// ✅ map, filter, reduce — HOF
const numbers = [1, 2, 3, 4, 5];

const doubled = numbers.map(n => n * 2);
console.log(doubled); // [2, 4, 6, 8, 10]

const evens = numbers.filter(n => n % 2 === 0);
console.log(evens); // [2, 4]

const sum = numbers.reduce((acc, n) => acc + n, 0);
console.log(sum); // 15

// ✅ Создание собственной HOF
function compose(f, g) {
  return (x) => f(g(x));
}

const add5 = (x) => x + 5;
const multiply2 = (x) => x * 2;

const add5ThenMultiply2 = compose(multiply2, add5);
add5ThenMultiply2(3); // (3 + 5) * 2 = 16

5. Function Composition (Комбинирование функций)

Создание сложной функции из простых.

// ✅ Композиция
const compose = (...fns) => (x) => fns.reduceRight((acc, fn) => fn(acc), x);

const add5 = (x) => x + 5;
const multiply2 = (x) => x * 2;
const subtract3 = (x) => x - 3;

const pipeline = compose(subtract3, multiply2, add5);
pipeline(1);
// 1 → add5 → 6
// 6 → multiply2 → 12
// 12 → subtract3 → 9

// ✅ Pipe (слева направо, отличается от compose)
const pipe = (...fns) => (x) => fns.reduce((acc, fn) => fn(acc), x);

const pipeline2 = pipe(add5, multiply2, subtract3);
pipeline2(1); // Одно и то же с compose, но порядок иной

6. Avoiding Side Effects (Избежание побочных эффектов)

Большинство побочных эффектов должны быть изолированы на краях приложения.

// ❌ Побочный эффект в функции
function saveUser(user) {
  const json = JSON.stringify(user); // ОК
  fetch('/api/users', { method: 'POST', body: json }); // ❌ Побочный эффект
  console.log('User saved'); // ❌ Побочный эффект
}

// ✅ Чистая функция, побочный эффект отделен
function serializeUser(user) {
  return JSON.stringify(user); // Чистая
}

// Побочный эффект в отдельной функции (на краю)
function persistUser(user) {
  const json = serializeUser(user);
  fetch('/api/users', { method: 'POST', body: json });
}

7. Currying (Карирование)

Трансформация функции, которая принимает несколько аргументов, в последовательность функций с одним аргументом.

// ✅ Карирование
const add = (a) => (b) => a + b;

add(2)(3); // 5

// ✅ Может быть полезно для частичного применения
const add2 = add(2);
add2(3); // 5
add2(5); // 7

// ✅ Автоматическое карирование
function curry(fn) {
  const arity = fn.length; // Количество параметров
  
  return function curried(...args) {
    if (args.length >= arity) {
      return fn(...args);
    } else {
      return (...nextArgs) => curried(...args, ...nextArgs);
    }
  };
}

const multiply = (a, b, c) => a * b * c;
const curriedMultiply = curry(multiply);

curriedMultiply(2)(3)(4); // 24
curriedMultiply(2, 3)(4); // 24

Функциональное программирование в JavaScript фреймворках

React

// ✅ React поощряет FP подход
function MyComponent({ items, onAdd }) {
  return (
    <ul>
      {items.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

// ✅ Чистые компоненты (functional components)
const Button = ({ onClick, children }) => (
  <button onClick={onClick}>{children}</button>
);

// ✅ Хуки (функции, а не классы)
function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  );
}

Array methods в FP стиле

// ✅ Цепочка функций
const users = [
  { name: 'Alice', age: 25, active: true },
  { name: 'Bob', age: 30, active: false },
  { name: 'Charlie', age: 22, active: true }
];

const result = users
  .filter(user => user.active) // Отфильтруем неактивных
  .map(user => ({ ...user, age: user.age + 1 })) // Увеличим возраст
  .sort((a, b) => a.age - b.age); // Отсортируем

// ✅ Reduce для сложных преобразований
const summary = users.reduce((acc, user) => {
  return {
    ...acc,
    totalAge: acc.totalAge + user.age,
    count: acc.count + 1,
    averageAge: (acc.totalAge + user.age) / (acc.count + 1)
  };
}, { totalAge: 0, count: 0, averageAge: 0 });

Преимущества функционального подхода

  1. Предсказуемость — чистые функции дают одинаковый результат
  2. Тестируемость — легко тестировать чистые функции
  3. Переиспользуемость — функции легко комбинировать
  4. Параллелизм — неизменяемость упрощает многопоточность
  5. Отладка — побочные эффекты локализованы
  6. Performance — можно оптимизировать с memoization

Недостатки

  1. Обучаемость — может быть непривычно для OOP разработчиков
  2. Performance — создание новых объектов может быть медленнее
  3. Код может быть более абстрактным
  4. Не все задачи хорошо решаются функциональным подходом

Резюме

Основные концепции FP:

  1. Чистые функции (pure functions)
  2. Неизменяемость (immutability)
  3. Функции как значения (first-class functions)
  4. Высшего порядка функции (higher-order functions)
  5. Комбинирование (composition)
  6. Избежание побочных эффектов

Функциональный подход в JavaScript не значит писать 100% чистый код — это баланс между FP и практичностью. Используй FP принципы где они улучшают код, но не переусложняй.

Что такое функциональный подход? | PrepBro