Замыкания: понятие и практическое применение

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

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

function outer() {
  let counter = 0;  

  function inner() {
    counter++;
    console.log(counter);
  }  

  return inner;
}  

const myClosure = outer();
myClosure(); // 1
myClosure(); // 2

Здесь inner помнит переменную counter и может изменять её, даже после завершения outer.


Как работают замыкания? 🧠

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

function createGreeting(greeting) {
  return function(name) {
    console.log(`${greeting}, ${name}!`);
  };
}  

const sayHello = createGreeting("Hello");
const sayHi = createGreeting("Hi");  

sayHello("Alice"); // "Hello, Alice!"
sayHi("Bob");      // "Hi, Bob!"

Функции sayHello и sayHi сохраняют значения greeting благодаря замыканию.


Зачем нужны замыкания? 💡

  1. Хранение состояния между вызовами
    Например, создание счётчиков или кэширующих функций.

  2. Сокрытие данных (инкапсуляция)
    Замыкания позволяют создавать приватные переменные.

  3. Функциональное программирование
    Каррирование, мемоизация, фабричные функции.


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

1. Счётчик с замыканием

function createCounter() {
  let count = 0;  

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

const counter = createCounter();
counter.increment();
counter.increment();
console.log(counter.getCount()); // 2

Здесь count скрыт от внешнего мира и доступен только через методы.

2. Мемоизация (кэширование)

function memoize(fn) {
  const cache = {};  

  return function(...args) {
    const key = JSON.stringify(args);  

    if (cache[key]) {
      console.log("Returning cached result");
      return cache[key];
    }  

    const result = fn(...args);
    cache[key] = result;
    return result;
  };
}  

const slowCalculation = (x) => x * 2;
const fastCalculation = memoize(slowCalculation);  

console.log(fastCalculation(5)); // 10 (вычислено)
console.log(fastCalculation(5)); // 10 (из кэша)

3. Каррирование

function multiply(a) {
  return function(b) {
    return a * b;
  };
}  

const multiplyByTwo = multiply(2);
console.log(multiplyByTwo(3)); // 6

Ловушки замыканий ⚠️

1. Циклы и замыкания

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 1000);
} // Выведет 3, 3, 3

Решение: используйте let (блочная область видимости) или IIFE.

for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 1000);
} // Выведет 0, 1, 2

2. Утечки памяти

Замыкания могут сохранять ссылки на большие объекты даже после завершения работы.

function heavyFunction() {
  const bigData = new Array(1000000).fill("data");  

  return function() {
    console.log("I have access to bigData!");
  };
}  

const holdMemory = heavyFunction();
// bigData остаётся в памяти, пока существует holdMemory

Совет: освобождайте ссылки на ненужные данные, если они больше не нужны.


Итог 🎯

Замыкания — мощный инструмент, который:
- ✅ Позволяет сохранять состояние.
- ✅ Создаёт приватные переменные.
- ✅ Используется в каррировании, мемоизации и модульном коде.

Попробуйте применить их в реальных проектах — например, для управления состоянием или оптимизации вычислений! 🚀

Скрыть рекламу навсегда

🧠 Учёба без воды и зубрёжки

Закрытый Boosty с наработками опытного преподавателя.

Объясняю сложное так, чтобы щелкнуло.

🚀 Забрать доступ к Boosty