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

В чём разница между HTTP 1.0, 1.1, 2.0?

2.3 Middle🔥 171 комментариев
#Сети и протоколы

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

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

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

Closures в JavaScript: Полное объяснение

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

Основной принцип

// Простейший пример
function outer() {
  const message = 'Hello'; // переменная outer scope
  
  function inner() {
    console.log(message); // inner имеет доступ к message
  }
  
  return inner;
}

const greet = outer();
greet(); // "Hello"
// Даже после того как outer() закончила работу,
// inner всё ещё имеет доступ к message

Это и есть closure: inner функция «закрыла» переменную message в себе.

Как это работает?

Scope Chain (цепь областей видимости)

Каждая функция в JavaScript имеет доступ к:

  1. Своим локальным переменным
  2. Переменным родительской функции
  3. Глобальным переменным
const global = 'I am global';

function level1() {
  const l1 = 'Level 1';
  
  function level2() {
    const l2 = 'Level 2';
    
    function level3() {
      console.log(l2);     // Доступ к level2 scope
      console.log(l1);     // Доступ к level1 scope
      console.log(global); // Доступ к global scope
    }
    
    return level3;
  }
  
  return level2();
}

const fn = level1();
fn(); // Всё работает!

Практические примеры

1. Data Privacy — приватные данные

// Без closure
class BankAccount {
  balance = 1000; // Публичная! Можно изменить
}

const account = new BankAccount();
account.balance = 999999; // Упс!

// С closure
function createBankAccount(initialBalance) {
  let balance = initialBalance; // Приватная переменная
  
  return {
    deposit(amount) {
      balance += amount;
      return balance;
    },
    withdraw(amount) {
      if (amount > balance) throw new Error('Insufficient funds');
      balance -= amount;
      return balance;
    },
    getBalance() {
      return balance;
    }
  };
}

const account = createBankAccount(1000);
account.deposit(500); // 1500
account.withdraw(200); // 1300
// balance напрямую не может быть изменена!
console.log(account.balance); // undefined

2. Function Factories — создание функций

// Factory для создания функций с разными параметрами
function createMultiplier(multiplier) {
  return function(number) {
    return number * multiplier;
  };
}

const double = createMultiplier(2);
const triple = createMultiplier(3);
const tenTimes = createMultiplier(10);

console.log(double(5));     // 10
console.log(triple(5));     // 15
console.log(tenTimes(5));   // 50

// Каждая функция «помнит» свой multiplier

3. Callbacks и Event Handlers

// Общая проблема: потеря контекста
const buttons = document.querySelectorAll('button');

buttons.forEach((button, index) => {
  button.addEventListener('click', function() {
    console.log(`Button ${index} clicked`); // index сохраняется в closure
  });
});

// Без closure (var проблема):
for (var i = 0; i < buttons.length; i++) {
  buttons[i].addEventListener('click', function() {
    console.log(`Button ${i} clicked`); // Всегда выведет последний i!
  });
}

// С closure (правильно):
for (let i = 0; i < buttons.length; i++) { // let создаёт closure для каждой итерации
  buttons[i].addEventListener('click', function() {
    console.log(`Button ${i} clicked`); // Правильное i для каждой кнопки
  });
}

4. Декораторы и Higher-Order Functions

// HOF: функция, которая возвращает функцию
function withLogging(fn) {
  return function(...args) {
    console.log(`Calling ${fn.name} with:`, args);
    const result = fn(...args);
    console.log(`Result:`, result);
    return result;
  };
}

function add(a, b) {
  return a + b;
}

const addWithLogging = withLogging(add);
addWithLogging(2, 3); // Logs всё

// Практический пример: кэширование
function memoize(fn) {
  const cache = {}; // Приватный кэш благодаря closure
  
  return function(...args) {
    const key = JSON.stringify(args);
    
    if (key in cache) {
      console.log('Cache hit!');
      return cache[key];
    }
    
    const result = fn(...args);
    cache[key] = result;
    return result;
  };
}

function expensive(n) {
  return n * n * n;
}

const memoizedExpensive = memoize(expensive);
console.log(memoizedExpensive(5)); // Вычисляет
console.log(memoizedExpensive(5)); // Cache hit!

5. Module Pattern

const Calculator = (function() {
  // Приватные переменные и методы
  let history = [];
  
  function logOperation(operation, a, b, result) {
    history.push({ operation, a, b, result });
  }
  
  // Публичный API
  return {
    add(a, b) {
      const result = a + b;
      logOperation('add', a, b, result);
      return result;
    },
    
    subtract(a, b) {
      const result = a - b;
      logOperation('subtract', a, b, result);
      return result;
    },
    
    getHistory() {
      return history;
    }
  };
})();

Calculator.add(5, 3);          // Логирует
Calculator.subtract(10, 4);    // Логирует
console.log(Calculator.getHistory()); // Видишь историю
// history недоступна напрямую! Это приватное

6. Async операции с сохранением контекста

function fetchUser(userId) {
  const startTime = Date.now();
  
  return fetch(`/api/users/${userId}`)
    .then(response => response.json())
    .then(data => {
      const duration = Date.now() - startTime; // closure на startTime
      console.log(`Loaded user ${userId} in ${duration}ms`);
      return data;
    });
}

// Или с async/await
async function fetchUserAsync(userId) {
  const startTime = Date.now(); // closure на startTime
  const response = await fetch(`/api/users/${userId}`);
  const data = await response.json();
  const duration = Date.now() - startTime;
  console.log(`Loaded user ${userId} in ${duration}ms`);
  return data;
}

Потенциальные проблемы

Memory Leaks через closures

// ❌ Проблема: большой объект остаётся в памяти
function createProcessor(hugeData) {
  return function processSmallPart(index) {
    return hugeData[index]; // Весь hugeData в памяти!
  };
}

// ✅ Решение: сохраняй только нужное
function createProcessor(hugeData) {
  const processed = hugeData.filter(/* ... */);
  hugeData = null; // Очистить
  
  return function processSmallPart(index) {
    return processed[index];
  };
}

Замыкание переменных в циклах (var vs let)

// ❌ var: все функции ссылаются на одну i
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100); // 3, 3, 3
}

// ✅ let: каждая функция имеет свою i (block scope)
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100); // 0, 1, 2
}

// ✅ Или функция-обёртка
for (var i = 0; i < 3; i++) {
  (function(j) {
    setTimeout(() => console.log(j), 100); // 0, 1, 2
  })(i);
}

Ключевые моменты

  • Closure создаётся автоматически при создании функции
  • Closure сохраняет переменные из родительской области видимости
  • Closure позволяет создавать приватные переменные
  • Closure используется в callbacks, HOF, модулях, декораторах
  • Нужно следить за memory leaks при хранении больших объектов
  • let/const безопаснее чем var при использовании closures