Взгляните на этот код: -

Это модуль в JavaScript. Чтобы модуль появился или был создан модуль, должна быть вызвана функция CoolModule. Когда вызывается CoolModule, создается внутренняя область видимости (помните, что область действия - это набор правил доступа к идентификаторам), и могут происходить замыкания. Закрытия здесь наблюдаются путем выполнения foo.doSomething(); и foo.doAnother();. Это связано с тем, что doSomething() и doAnother() вызываются вне их лексической области видимости (где они были объявлены во время автора) и все еще могут обращаться к переменным внутри функции CoolModule.

CoolModule также возвращает объект, содержащий некоторые ссылки на наши внутренние функции. Возвращаемое значение объекта можно рассматривать как общедоступный API, потому что мы можем использовать информацию от объекта для доступа к модулю и получить что-то обратно, а это просто то, что делает API (используйте код, API, чтобы получить доступ к некоторым данным и получить обратно результат )

Есть еще один модуль, который можно вызвать только один раз:

Обратите внимание, что при использовании анонимной функции CoolModule вызывается без вызова переменной foo. Это связано с тем, что, когда вы даете переменной значение, значение сначала выполняется, если оно должно быть, т.е. в var a = 2; значение 2 не должно выполняться.

Современные модули
Зависимость модулей - это набор кода, который необходим модулям для работы. Например, если библиотеке A требуется библиотека B для работы, тогда библиотека B является зависимостью для библиотеки A. Загрузчик / менеджер зависимостей модулей - это набор кода, который управляет или загружает зависимости модулей.

Рассмотрим этот код: -

Давайте посмотрим, что происходит.
MyModules получает из возвращаемого значения IIFE объект с двумя свойствами. MyModules предназначен для создания модулей с использованием функций define и get . Давайте посмотрим на вторую часть кода, чтобы понять больше: -

Мы будем действовать постепенно. Мне было трудно понять, что происходит.
В настоящее время мы изучаем код с bar в качестве первого параметра define. MyModules.define(...) используется для создания модуля. Давайте разберемся, как он это делает. define принимает 3 аргумента: -
1. имя модуля
2. пустой массив
3. выражение функции

Посмотрим, как define обрабатывает эти аргументы.

В IIFE, где определена наша функция define (каламбур не предназначен), есть цикл for, но цикл for не выполняется, потому что deps.length == 0. И что дальше. Эта строка modules[name] = impl.apply( impl, deps ); Прежде чем мы продолжим, не беспокойтесь, если вы еще не понимаете, как работает this ключевое слово, хотя apply его использует, оно нам здесь не нужно (продолжайте понимать, почему). apply принимает два параметра, первый - это this для impl (функция перед apply), а второй параметр - это аргумент, который принимает impl. Первый параметр ни на что не влияет, потому что функция, третий параметр define, не имеет ссылки на ключевое слово this (поэтому вам не нужно понимать this здесь).

В дальнейшем modules[name] = impl.apply( impl, deps ); выполняется как modules["bar"] = function(){}.apply( function(){}, []);, потому что impl содержит третью функцию define. Обратите внимание, что function(){} - это третья функция define. Также apply вызывает любую функцию, стоящую перед ним. По сути, apply вызывает выражение анонимной функции с [], которое пусто, передается в качестве аргумента анонимной функции (так что на самом деле аргумент не передается). Затем возвращаемое значение, которое является объектом с одним свойством, hello, и значением, которое ссылка на функцию hello. Это возвращаемое значение затем передается в modules["bar"].

По сути, то, что сейчас есть в нашем modules объекте, выглядит примерно так { bar: {hello: function hello()} }

Посмотрим, что происходит во втором MyModules.define(..);. У нас есть 3 аргумента: -
1. Имя модуля - "foo"
2. Один элемент в массиве deps - "bar"
3. Выражение функции

Здесь цикл for выполняется один раз, и выполняется следующее:
deps[0] = modules["bar"]; Элемент внутри deps[0] изменяется на значение modules["bar"], которое является свойством, которое мы недавно добавили в modules; {hello: function hello()}. Затем выполняется modules[name] = impl.apply( impl, deps );, поскольку
modules["foo"] = function(bar){}.apply( function(bar), {hello: function hello()} );
apply будет выполнять выражение функции в начале со значением deps, {hello: function hello()} в качестве аргумента. Когда apply выполняется, как указано, возвращается объект awesome: awesome. Теперь наш объект modules выглядит примерно так:
{ bar: {hello: function hello()}, foo: {awesome: function awesome()} }

Давайте перейдем к строке var bar = MyModules.get( "bar" );. Помните, IIFE, назначенный переменной MyModules, возвращает объект с двумя свойствами. Итак, MyModules.get( "bar" ); обращается к функции get, которая затем возвращает значение modules["bar"];, которое затем присваивается переменной bar . Помните, что modules["bar"] значение равно {hello: function hello()}. Что касается следующей строки var foo = MyModules.get( "foo" );, get также вызывается и возвращает modules[ "foo" ], значение которого равно {awesome: function awesome()}, которое затем присваивается переменной foo.

Теперь значения bar и foo равны {hello: function hello()} и {awesome: function awesome()} соответственно. Затем выполняется console.log( bar.hello( "hello" ) );. Что тогда произойдет, когда будет выполнен bar.hello( "hello" )? Закрытия случаются. Помните, замыкание - это когда внутренняя функция может получить доступ к своей лексической области видимости, даже если она выполняется за пределами своей лексической области видимости. Здесь, когда bar.hello() выполняется, hello(), которая является внутренней функцией, может получить доступ к своей лексической области видимости и выполняться, даже если она вызывается за пределами своей лексической области. Это закрытия.

Двигаясь дальше, hello( "hippo" ) возвращает «Разрешите представить:» + «бегемотик»
Затем выполняется foo.awesome(); и также наблюдаются замыкания, потому что awesome(), внутренняя функция, может получить доступ к своей лексической области, даже если она была выполнена за пределами его лексический объем. Таким образом, когда выполняется awesome(), он выходит из системы bar.hello( "hippo").toUpperCase(). Обратите внимание, что bar.hello( "hippo" ) совпадает с тем, что мы вышли из системы перед выполнением foo.awesome(). Следовательно, bar.hello( "hippo").toUpperCase() берет строку «Разрешите мне представить: hippo», переводит ее в верхний регистр и выводит из системы.

Есть еще один случай закрытия. Сможете ли вы его найти?
Mymodules.define(...) и Mymodules.get(...), потому что это обе внутренние функции, которые также могут получить доступ к своей лексической области видимости, даже если они были вызваны вне своей лексической области видимости.

Вопросы однозначно приветствуются =)