Ищите переменную по месту создания функции
Лексическая область видимости определяется структурой программы. Вложенная функция может обращаться к переменной внешней функции, если та входит в доступное окружение. Место последующего вызова не переносит её автоматически в новый набор локальных переменных. Полезно мысленно разделить два вопроса: где функция создана и где выполняется сейчас. Это помогает разбирать обработчики событий и функции, возвращаемые из других функций. Одного совпадения имени переменной в вызывающем коде недостаточно, чтобы изменить её значение внутри замыкания.
Различайте связь с переменной и снимок значения
Представьте внешнюю функцию, которая создаёт счётчик с начальным значением ноль и возвращает внутреннее действие увеличения. При каждом вызове это действие меняет доступную ему переменную и возвращает новый результат. Замыкание сохраняет связь с состоянием, а не просто фотографию первоначального нуля. Поэтому повторные вызовы дают разные числа. Если другое доступное действие меняет ту же переменную, это изменение может быть видно остальным функциям из того же окружения. Для анализа нужно знать, какие функции разделяют состояние.
Создавайте независимые окружения осмысленно
Два отдельных вызова фабрики счётчиков создают два отдельных состояния. Первый счётчик может уже дойти до трёх, пока второй только возвращает единицу. Это отличается от ситуации, когда две ссылки указывают на одну и ту же функцию с общим окружением. Подпишите созданные экземпляры на бумаге и проследите, к какой переменной относится каждый вызов. Такой приём помогает разбирать не только счётчики, но и настройки обработчиков, небольшие кеши и отложенные действия. Общее состояние должно появляться намеренно, а не случайно.
Проверяйте циклы и время выполнения
Если несколько отложенных функций используют одну изменяемую переменную цикла, к моменту их вызова она может уже иметь другое значение. Поэтому важно понять, создаётся ли отдельная привязка для каждого шага или все действия обращаются к общей. В современных конструкциях с let в цикле можно получить отдельные привязки для итераций; поведение var отличается. При этом замыкание само по себе не означает асинхронность. Оно описывает доступ к окружению, а момент вызова задаётся остальной программой. Эти два механизма следует проверять отдельно.
Попробуйте на практике
Проследите состояние двух вымышленных счётчиков, созданных одной фабрикой замыканий.
- Нарисуйте два отдельных окружения А и Б, в каждом начальное значение равно нулю. Каждый вызов увеличивает своё значение на один и возвращает его.
- Выполните последовательность вызовов А, А, Б, А. После каждого запишите оба текущих значения.
- Теперь представьте, что имя Б является второй ссылкой на тот же счётчик А. Повторите последовательность и сравните результаты.
- Объясните различие через число созданных окружений, а не через различие имён переменных.
Как проверить результат. Для независимых счётчиков результаты равны один, два, один, три. Для двух ссылок на один счётчик получаются один, два, три, четыре.
Частые вопросы
Любое замыкание выполняется позже основного кода?
Нет. Функцию можно вызвать сразу или позднее. Замыкание относится к доступному окружению, а не к расписанию выполнения.
Отдельное имя всегда означает отдельное состояние?
Нет. Несколько имён могут ссылаться на одну функцию. Независимость определяется созданием окружения и связями объектов, а не названием ссылки.
Самостоятельный разбор темы. Содержание конкретной обучающей программы здесь не представлено.