Замыкание - это комбинация функции и лексического окружения, в котором эта функция была объявлена. Среда может состоять как из локальных переменных, объявленных в теле функции, так и из глобальных переменных, объявленных вне функции. Чтобы выразить это в уравнении, Closure равно функции плюс лексическое окружение (например, локальные переменные, глобальные переменные, внешние функции), в котором эта функция была объявлена.

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

В приведенном ниже примере показано, что внутренняя анонимная функция имеет доступ к локальной переменной double вне своей области (лексической области). Мы привязываем эту функцию к различным переменным за пределами этой области, и каждый раз, когда мы их вызываем, мы можем передавать разные значения в качестве аргументов. Как только анонимная функция получит доступ к внутренней переменной double, она теперь может принимать как значение этой переменной, так и передавать аргумент и производить простые вычисления (val * double).

function simpleDoubler(){
    let double = 2; // local variable of lexical environment
    return function (val){
       return val * double;
 }
}
let doubleFour = simpleDoubler();
let doubleTen = simpleDoubler();
console.log(doubleFour(4)); // 8
console.log(doubleTen(10)); // 20

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

let colorChanger = (function() {
    return function change(color) {
console.log({color});
}})();
let red = colorChanger('red');
let green = colorChanger('green');
let orange = colorChanger('orange');

В приведенном выше примере мы создали функцию с именем colorChanger, которая возвращает другую функцию, в которую мы можем передать аргумент. Наша внутренняя функция регистрирует в консоли объект с синтаксическим сахаром свойства объекта (функция, доступная в ES6 / ES2015), где, если ключ и значение имеют одинаковое значение (например, { color: color}), мы можем избежать дублирования, просто передав имя значения ({color}). Здесь важно отметить, что это IIFE (выражение с немедленным вызовом функции), которое запускается сразу после объявления, передавая обе функции в круглые скобки и вызывая их сразу после них (опять же, в круглых скобках), таким образом вызывая их правильно. после того, как он был создан.

let country = (() => {
  return (name) => {
   return (capital) => {
    return (language) => {
     return (population) => {
      console.log(`country: ${name}, capital: ${capital}, language: ${language}, population: ${population}`);
  if (population < 3000000) {
    console.log('small population');
  } else if (population >= 3000000 && population <= 100000000) {
    console.log('mid-size population');
  } else {
    console.log('large population');
      }
     };
    };
   };
  };
})();
let malta = country('Malta')('Valletta')('Maltese')(514564);
let france = country('France')('Paris')('French')(65273511);
let usa = country('USA')('Washington')('English')(331002651);

Пример выше немного сложнее. Существует целая иерархия вложенных функций, но все будет в порядке, как только мы вспомним самую важную вещь в Closures: чем глубже область видимости, тем больше у нее доступа к внешним областям. Это означает, что анонимная стрелочная функция, которая принимает совокупность в качестве аргумента, имеет доступ ко всем аргументам, переданным во внешних функциях до (language, затем заглавная, затем имя). После написания функции country нам нужно привязать ее к различным переменным, таким как malta, france и usa. Аргумент последовательность, который мы передаем в функцию country, также важен. Сначала в круглых скобках мы передаем строку Malta, затем строку Valletta, затем строку Maltese, затем целое 514564 ( которое является фактическим населением острова Мальта на момент написания этой статьи). Они представляют собой заполнители имя, заглавная, язык и совокупность соответственно (вот почему важна последовательность ). Оператор if в коде на первый взгляд может показаться полностью необязательным, поскольку он обеспечивает дополнительную сортировку по размеру совокупности, но если мы попытаемся вырезать и вставить это условие где-нибудь за пределами его текущей области (ядро, самая глубокая область видимости), он выдаст ошибку (Uncaught ReferenceError: Population не определен), жалуясь, что совокупность не видна, пока на нее ссылаются снаружи, но она была создана внутри! Это доказывает концепцию замыканий, согласно которой только внутренняя область видимости имеет доступ ко всем данным (переменным, аргументам) лексической среды, а не наоборот!

let country = (() => {
  if (population < 3000000) {
   console.log('small population');
  } else if (population >= 3000000 && population <= 100000000) {
   console.log('mid-size population');
  } else {
   console.log('large population');
  }
  return (name) => {
   return (capital) => {
    return (language) => {
     return (population) => {
    console.log(`country: ${name}, capital: ${capital}, language: ${language}, population: ${population}`);
    };
   };
  };
 };
})();
let malta = country('Malta')('Valletta')('Maltese')(514564);
let france = country('France')('Paris')('French')(65273511);
let usa = country('USA')('Washington')('English')(331002651);
// Uncaught ReferenceError: population is not defined

Заключение

Хотя многие разработчики JavaScript используют Closures инстинктивно или на основе опыта, было бы лучше, если бы мы поняли, что концепция будет более последовательной, разумной и уверенной. Более глубокая область видимости имеет доступ к более поверхностной, или внутренняя область имеет доступ к внешней. С этого момента, поскольку вы уже прошли одну из самых сложных частей в JavaScript, ничто не должно удерживать вас в вашем путешествии, чтобы раскрыть все секреты и красоту этого языка сценариев и получить максимальную отдачу от одной из его причудливых частей, называемых Closures.