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

Закрытие создается, когда внутренняя функция ссылается на переменную из внешней функции. Внутренняя функция сохраняет ссылку на переменную внешней функции, и эта ссылка называется замыканием.

Зачем использовать замыкания?

Замыкания полезны, поскольку они позволяют разрабатывать приватные переменные и функции в JavaScript. Доступ к таким переменным и функциям возможен только в рамках функции, в которой они были определены.

Функция замыканий сделает ваш код очень понятным и модульным. Предположим, что необходимо сохранить счетчик внутри функции, гарантируя при этом, что переменная счетчика недоступна извне функции. В этом случае замыкание может быть использовано для создания конфиденциальной переменной счетчика.

Частные переменные в замыканиях

Допустим, у нас есть функция, которая должна отслеживать счетчик, однако мы не хотим, чтобы счетчик был доступен извне функции. Мы можем использовать замыкание для создания частной переменной-счетчика, например:

function counter() {
  let myCounter = 0
  
  return function() {
    myCounter++;
    console.info(myCounter, 'myCounter')
  }
}
const increaseCounter = counter()
increaseCounter() // 1
increaseCounter() // 2
increaseCounter() // 3

Давайте немного взглянем на код: функция handleClick создает вложенную функцию, которая может получить доступ к переменной count. Чтобы использовать эту функцию в качестве обратного вызова для метода addEventListener, мы присваиваем ее переменной incrementClickCount. При каждом нажатии кнопки вызывается функция incrementClickCount, и значение переменной count увеличивается. Поскольку переменная count определена внутри функции handleClick, доступ к ней вне функции невозможен, что делает ее закрытой переменной.

Слушатели событий в замыканиях

Замыкания также чаще всего используются с прослушивателями событий. Допустим, у нас есть кнопка, к которой мы хотим добавить прослушиватель событий щелчка, однако мы хотим отслеживать, сколько раз кнопка была нажата. Мы можем использовать замыкание, чтобы создать приватную переменную cliced и прикрепить прослушиватель событий к кнопке следующим образом:

<button id="myButton">Click me</button>
<script>
  const myButton = document.getElementById('myButton');
  
  function handleClick() {
    let clicked = 0;
    
    return function() {
      clicked++;
      console.info(`Button clicked ${clicked} times`);
    }
  }
  
  const increaseClickCount = handleClick();
  
  myButton.addEventListener('click', increaseClickCount);
</script>

В этом примере функция handleClick возвращает внутреннюю функцию, которая имеет доступ к переменной clicked. Мы устанавливаем переменную increaseClickCount в возвращаемую внутреннюю функцию, чтобы мы могли передать ее в качестве функции обратного вызова методу addEventListener.

Всякий раз, когда мы нажимаем кнопку, increaseClickCount функция будет выполняться, тогда наша переменная clicked будет увеличена. Поскольку переменная clicked объявлена ​​внутри функции handleClick, она недоступна извне функции, что делает ее частной переменной.

Реализация частного модуля

Замыкания также можно использовать для создания частных модулей в JavaScript. Приватный модуль — это модуль, который имеет приватные переменные и функции, недоступные снаружи модуля.

const calculator = (function() {
  let result = 0;
  function add(x) {
    result += x;
  }
  function subtract(x) {
    result -= x;
  }
  function multiply(x) {
    result *= x;
  }
  function divide(x) {
    result /= x;
  }
  function getResult() {
    return result;
  }
  return {
    add: add,
    subtract: subtract,
    multiply: multiply,
    divide: divide,
    getResult: getResult
  }
})();
calculator.add(5);
calculator.multiply(2);
console.info(calculator.getResult()); // prints 10

В этом блоке кода переменная calculator назначается немедленно вызываемому функциональному выражению (IIFE), которое возвращает объект, содержащий функции, которые можно использовать для выполнения арифметических операций. Переменная result объявлена ​​внутри закрытия IIFE и недоступна снаружи модуля calculator. Это позволяет нам создать приватный модуль с приватными переменными и функциями, доступ к которым можно получить только через функции, которые возвращаются из модуля.

Эти примеры демонстрируют гибкость и полезность замыканий в разработке JavaScript. Используя возможности замыканий, мы можем создавать более модульный, удобный и эффективный код.

Реализация функций обратного вызова

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

function waitThenDo(callback, time) {
  setTimeout(function() {
    callback();
  }, time);
}
function sayHello() {
  console.info('Hello!');
}
waitThenDo(sayHello, 1000); // waits 1 second before logs 'Hello!'

В этом примере функция waitThenDo() принимает два аргумента.

1-) функция обратного вызова

2-) время в миллисекундах.

Он использует метод setTimeout() для задержки выполнения функции обратного вызова на указанное время. По истечении времени запускается функция обратного вызова. В этом случае функция sayHello() выполняется как функция обратного вызова и запускается с задержкой в ​​1 секунду.

Создание мемоизированной функции в замыканиях

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

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

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

      if (cache[key]) {
        console.info('Returning from cache...')

        return cache[key]
      }

      console.info('Calculating result...')
      const result = func.apply(this, args)
      cache[key] = result

      return result
    }
  }

  function expensiveFunction(x, y) {
    console.info('Calculating...')

    return x * y
}

const memoizedFunction = memoize(expensiveFunction)
console.log(memoizedFunction(5, 10)) // Calculating result...50
console.log(memoizedFunction(5, 10)) // Returning from cache...50
console.log(memoizedFunction(10, 20)) // Calculating result...200
console.log(memoizedFunction(10, 20)) // Returning from cache...200

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

Раскройте потенциал JavaScript с помощью этого подробного руководства по замыканиям для начинающих.

С помощью замыканий в JavaScript вы сможете вывести свои навыки кодирования на новый уровень и писать более безопасный, эффективный и модульный код.

Осваивая концепции и примеры замыканий в JavaScript, новички могут вывести свои навыки программирования на новый уровень и создавать более эффективные и мощные приложения JavaScript.