TLDR: функции могут возвращать дочерние функции. Закрытие — это когда дочерняя функция сохраняет доступ к своей родительской области.

Простое объяснение:

В JavaScript функции могут возвращать любую структуру данных, включая другие функции.

В нашем небольшом примере наша функция закрытия возвращает функцию changeCount. changeCount — это просто. Он обращается к переменной count и добавляет к ней единицу. Однако когда возвращается changeCount, он входит в новую область. Например, он может быть возвращен в глобальную область видимости main.js. Он должен потерять доступ к глобальной области видимости родительской функции.

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

Сложное объяснение

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

Если вы переназначите переменную, то ее прежняя ссылка будет потеряна. Потерянная структура данных всегда будет удалена потоком сборщика мусора JavaScript.

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

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