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

Как понять когда в коде должно быть замыкание?

2.3 Middle🔥 291 комментариев
#JavaScript Core

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

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

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

Когда в коде должно быть замыкание

Что такое замыкание

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

Основной случай: инкапсуляция и приватные переменные

Замыкание идеально подходит когда нужна приватная переменная, к которой нельзя получить прямой доступ:

// Хорошее использование замыкания для инкапсуляции
function createCounter() {
  let count = 0; // приватная переменная

  return {
    increment: () => {
      count++;
      return count;
    },
    decrement: () => {
      count--;
      return count;
    },
    getCount: () => count,
  };
}

const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
// counter.count нельзя изменить напрямую - это приватная переменная

Фабрики функций и параметризация

Замыкания используются для создания функций с предзаполненными параметрами:

// Функция, которая создаёт персонализированное приветствие
function makeGreeter(greeting) {
  return (name) => {
    return `${greeting}, ${name}!`;
  };
}

const sayHello = makeGreeter('Hello');
const saySalve = makeGreeter('Salve');

console.log(sayHello('Alice')); // Hello, Alice!
console.log(saySalve('Bob'));   // Salve, Bob!

Обработчики событий в React

В React очень часто используются замыкания для работы с событиями и состоянием:

import { useState } from 'react';

export function UserList() {
  const [users, setUsers] = useState([]);

  const handleDeleteUser = (userId) => {
    // Это замыкание - имеет доступ к userId из внешней области
    // и к setUsers из компонента
    setUsers((prevUsers) =>
      prevUsers.filter((user) => user.id !== userId)
    );
  };

  const handleEdit = (userId) => {
    // Ещё одно замыкание - имеет доступ к userId
    const user = users.find((u) => u.id === userId);
    console.log('Editing:', user);
  };

  return (
    <ul>
      {users.map((user) => (
        <li key={user.id}>
          {user.name}
          <button onClick={() => handleEdit(user.id)}>Edit</button>
          <button onClick={() => handleDeleteUser(user.id)}>Delete</button>
        </li>
      ))}
    </ul>
  );
}

Когда НЕ использовать замыкания

Не стоит использовать замыкания когда нужна простая логика без сохранения состояния:

// Плохо - ненужное замыкание
const add = (a) => (b) => a + b;
const result = add(5)(3); // 8

// Хорошо - просто функция
const add = (a, b) => a + b;
const result = add(5, 3); // 8

Работа с асинхронным кодом

Замыкания критичны при работе с setTimeout, setInterval и fetch:

// Правильное использование замыкания с асинхронностью
function setupButtonHandler(buttonId, apiUrl) {
  const button = document.getElementById(buttonId);
  let requestCount = 0;

  button.addEventListener('click', async () => {
    requestCount++;
    const currentCount = requestCount;

    try {
      const response = await fetch(apiUrl);
      // Замыкание имеет доступ к currentCount и requestCount
      if (currentCount === requestCount) {
        // Игнорируем устаревшие ответы
        const data = await response.json();
        console.log(data);
      }
    } catch (error) {
      console.error('Request failed:', error);
    }
  });
}

Паттерн: IIFE (Immediately Invoked Function Expression)

Замыкания используются в паттерне IIFE для избежания загрязнения глобального пространства имён:

// Каждый цикл будет иметь свой counter благодаря замыканию
for (let i = 0; i < 3; i++) {
  (function(counter) {
    setTimeout(() => {
      console.log(`Iteration ${counter}`);
    }, 1000);
  })(i);
}
// Выведет: Iteration 0, Iteration 1, Iteration 2 (с задержкой)

Кэширование и мемоизация

Замыкания идеальны для реализации кэширования:

function createMemoizer(fn) {
  const cache = {}; // приватное хранилище

  return (arg) => {
    if (arg in cache) {
      console.log('From cache:', arg);
      return cache[arg];
    }
    const result = fn(arg);
    cache[arg] = result;
    return result;
  };
}

const expensiveFunction = (n) => {
  console.log('Computing...');
  return n * 2;
};

const memoized = createMemoizer(expensiveFunction);
memoized(5); // Computing... 10
memoized(5); // From cache: 5, 10

Выводы

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

Как понять когда в коде должно быть замыкание? | PrepBro