Какая польза от внутренней функции, используемой снаружи внешней функции в JavaScript
Что проверяют
Понимаете ли вы замыкания не как определение из учебника, а как рабочий инструмент: где они дают пользу и где создают проблемы. Следом обычно просят написать счётчик или debounce и спрашивают, что выведет цикл с setTimeout.
Как отвечать
Функция в JavaScript запоминает лексическое окружение, в котором её создали. Когда внутреннюю функцию возвращают или передают наружу, она продолжает видеть переменные внешней, хотя та уже завершилась. Сохраняется не копия значения, а живая ссылка на переменную: если её изменить, функция увидит новое значение.
Где это полезно:
- приватное состояние:
function createCounter() { let count = 0; return () => ++count; }. Доcountснаружи не добраться, только через возвращённую функцию. На этом построен модульный паттерн, а в классах для той же цели есть поля#count; - фабрики функций и частичное применение:
makeAdder(5)возвращает функцию, которая помнит пятёрку. Так же устроено каррирование; - состояние между вызовами:
debounceиthrottleдержат id таймера в замыкании, мемоизация хранит кеш результатов; - обработчики и колбэки: обработчик клика помнит id элемента или данные, для которых его создали;
- React-хуки: каждый рендер создаёт новые функции, которые замыкают
propsи состояние именно этого рендера. На этом держатсяuseCallbackи массивы зависимостей.
Цена тоже есть: пока жива ссылка на внутреннюю функцию, живут и переменные, которые она удерживает. Забытый обработчик события или незачищенный setInterval держит в памяти всё, что замкнул.
Где спотыкаются
Говорят, что замыкание копирует значения в момент создания. Отсюда неверный ответ про цикл: с var все таймеры выведут последнее значение, потому что переменная одна на всю функцию, а let создаёт новую привязку на каждой итерации.
Не узнают устаревшее замыкание в React: setInterval внутри useEffect с пустым массивом зависимостей навсегда видит начальное состояние. Лечится функциональным обновлением setCount(c => c + 1) или правильными зависимостями.
Сводят пользу к «сокрытию переменных» и не могут привести ни одного живого примера из своего кода.