JavaScript – язык программирования, который используется при создании веб-контента. Он предусматривает не только простой синтаксис, но и разнообразные функции. Все это помогает создавать действительно стоящие приложения.
Для того, чтобы быть хорошим разработчиком, необходимо изучить немало особенностей ДжаваСкрипт. Одна из них – это замыкания. Новичков в программировании соответствующая тема способна на первых порах сбить с толку. Но, если разобраться с оной, после выйдет создавать улучшенные программные коды.
Определение понятия
Замыкание – функция, которая обладает доступом к своей внешней функции по области видимости, даже если последняя остановилась. Соответственно, замыкание умеет запоминать и получать доступ к:
- переменным;
- аргументам.
И не важно, прекратилась ли выполняться внешняя функция или нет.
Замыкание – функция первого класса, имеющая в своем теле ссылки на переменные, объявленные за пределами соответствующей функции в зоне видимости и не являющиеся ее параметрами. Так называют функцию, ссылаемую на свободные переменные в области видимости.
Замыкание — способ представления функциональности и информации, которые связаны друг с другом. Данный момент сход с экземплярами объектов. Рассматриваемая «операция» — особый вид функции, которая имеет доступ (предопределение) в теле другой функции. Создается каждый раз при выполнения последней.
Лексическая область видимости
Так принято называть статическую область в JS, которая имеет прямое отношение к доступу к переменным, объектам, функциям. В основе лежит расположение соответствующих элементов в программном коде.
Выше представлен пример кода. Его нужно рассмотреть, чтобы лучше разобраться в лексической области видимости. Тут происходит следующее:
- Функция inner обладает доступом к переменным в пределах своей зоны видимости.
- Она же имеет доступ в пределах функции outer, а также глобальной области видимости.
- Функция outer получает доступ к переменным, которые объявлены в собственной и глобальных зонах.
Полученная цепочка обладает следующим представлением:
Обратить внимание необходимо на следующие факты:
- Функция Inner окружена лексической областью outer.
- Последняя окружена глобальной «видимостью».
- У функции inner есть доступ к переменным, которые определены в outer и глобальной видимости.
Но для того, чтобы продолжить тему и лучше понимать замыкания, рассмотрим в статье далее несколько примеров. Они всегда помогают успешно ориентироваться в выбранном направлении.
Примеры на практике
Внутреннее устройство замыканий понять очень трудно, особенно если опираться только на «примерные задачи». Наглядные примеры продемонстрируют нюансы рассматриваемой темы.
Номер один
Ниже представлен элемент программного кода, написанного в Java Script:
Здесь происходит следующее:
- Функция person() отвечает за возврат внутренней функции displayName().
- Происходит сохранение последней в переменную peter.
- После вызывается peter(), где соответствующая переменная содержит на displayName().
- Через консоль происходит ввод имени Peter.
Функция displayName() не обладает переменной с именем name. Из-за этого можно прийти к выводу о том, что соответствующая «операция» способна тем или иным методом получать доступ к переменной, объявленной во внешней по отношению к ней функции, даже когда последняя отработана. Дело все в том, что в приведенном примере displayName() – это замыкание.
Пример два
А вот еще один наглядный пример, который позволяет наглядно увидеть принцип работы рассматриваемой операции:
Здесь происходит следующее:
- В переменной count содержится ссылка на анонимную внутреннюю функцию, которая возвращается посредством getCounter().
- Count() выступает замыканием и может обращаться к одноименной переменной counter.
- Counter находится в getCount().
- Рассматриваемая «операция» может обращаться к переменной counter даже после прекращения работы getCounter().
Значение переменной counter не будет сбрасываться в 0 каждый раз, когда происходит вызов count(). Подобная ситуация возникает из-за того, что при активации соответствующей «операции» осуществляется создание новой области видимости. А для getCounter() имеется всего одна подобная «зона». Переменная counter объявлена в области видимости getCounter, значение оной между вызовом count() будет сохраняться без сброса.
Принцип работы
Настало время рассмотреть замыкания более подробно. Ранее было сказано лишь о том, что это такое, а также приведены наглядные примеры. Внутренние механизмы процесса не затрагивались.
Чтобы лучше понимать замыкания, требуется разобраться с весьма важными концепциями выбранного языка программирования. Речь идет о контексте и лексическом окружении.
О контексте выполнения
Это абстрактное окружение, в котором код JS вычисляется, а затем реализовывается. Здесь важно запомнить следующие моменты:
- При выполнении глобального кода манипуляции осуществляются внутри глобального контекста выполнения.
- Код «операции» будет «инициализироваться» внутри контекста выполнения оной.
- В определенный момент времени может обрабатываться код только в одном контексте выполнения. Связано это с тем, что JS является однопоточным языком.
- Управление процессами происходит через стек вызовов.
Стек вызовов – своеобразная структура информации, которая устроена по принципу LIFO (последний вошел – первым вышел). Новые составляющие помещаются исключительно в верхней части стека. И именно оттуда происходит изъятие оных.
Нынешний контекст выполнения всегда расположен в верхней части стека. Если текущая функция прекращает работать, ее контекст выполнения будет извлекаться из стека. Управление перейдет тому контексту, который был расположен ниже в стеке вызовов.
О процессе наглядно
Используем следующий пример кода, чтобы больше разобраться в стеках вызовов и контексте:
Здесь при выполнении кода:
- Движок создаст глобальный контекст выполнения для обработки глобальной кодификации.
- При встрече с вызовом функции first() будет создан новый контент для выполнения оной.
- Далее полученный контекст будет размещен для соответствующей «операции» в верхней части стека.
Стек вызовов представлен ниже:
При завершении first(), контекст ее выполнения будет извлекаться из стека вызовов. Это дает возможность передачи управления context выполнения, расположенному ниже него. А именно – глобального характера. Далее обрабатывается элемент представленной кодификации, находящийся в глобальной «территории».
Несколько слов о лексическом окружении
Когда JavaScript осуществляет создание контекста выполнения для реализации «операций» или глобальных частей кода, появляется новое лексическое окружение. Оно предназначается для того, чтобы хранить переменные, объявленные в той или иной функции во время обработки оной.
Лексическое окружение – структура информации, несущая в себе сведения о соответствии переменных с идентификаторами. Идентификатором выступает имя переменной или «операции». Переменная – ссылка на объект. Может являться значением примитивного характера.
В лексическом окружении функции содержатся некие составляющие:
- запись окружения – пространство, в котором хранятся объявления «манипуляций» и переменных;
- ссылка на внешнее окружение – своеобразная ссылка, которая позволяет обращаться к родительскому лексическому окружению.
Именно ссылки «внешнего типа» выступают в качестве основополагающей для полноценного понимания замыканий.
Выше представлен элемент кода, который описывает лексическое окружение.
На что стоит поглядеть
Вот код, который поможет лучше освоить «лексику»:
Здесь происходит следующее:
- Движок Джавы будет отвечать за создание глобального контекста выполнения для обработки глобального кода.
- После этого он создает новое лексическое окружения для того, чтобы хранить переменные и «операции», объявленные в глобальной области видимости.
- Результатом окажется лексическое окружение:
- Ссылка на внешнее окружение (outer) стоит в значении null. Связано это с тем, что у глобальной «области видимости» отсутствует «лексика».
- В процессе создания context для first() возникнет и «лексика» для хранения переменных объявленных в пределе оной и образованных при непосредственном выполнении.
- Итог получится таким:
- Ссылка на внешнюю «лексику» устанавливается в значении <globalLexicalEnvironment>. Связано это с тем, что исходный код «операции» расположен в «глобальном пространстве».
Стоит обратить внимание на то, что при завершении работы функции контекст будет извлекаться из стеков вызовов. Лексическое окружение может удаляться из памяти или оставаться в ней далее. Все зависит от существования ссылки на соответствующую «лексику» в виде ссылок на внешнее лексическое окружение в других рассматриваемых областях.
Внешние переменные
Локальные переменные – не единственные, к которым можно обратиться из функции. Дополнительно предусматривается вызов внешних «хранилищ»:
Интерпретатор при доступе к переменной будет в первую очередь пытаться обнаружить переменную в текущем LexicalEnvironment. Лишь после этого, при условии отсутствии оной – во внешнем объекте «хранилищ». В приведенном примере таковым выступает windows.
Подобное явление становится доступным из-за того, что ссылка на внешний объект переменных будет храниться в специальном свойстве функции внутреннего характера. А именно – в [[Scope]]. Оно не имеет прямого доступа, но это не значит, что программер должен обходить стороной оную. Знать принципы работы «Скоп» важно при программировании на JS.
Интерпретатор при создании «операции» создает для этой функции скрытое свойство [[Scope]]. Оно будет ссылаться на лексическое окружение, в котором оная создавалась.
Пример выше предусматривает такие явления:
- Упомянутый ранее окружением выступает window, что приводит к образованию свойства:
- Оное будет неизменным. Следует за «операцией», привязывая ее к своему месту рождения.
- После запуска ее объект переменных LocalEnvironment получит ссылку на внешнюю «лексику» со значением из «Скоп».
- Когда переменная не будет обнаружена в «манипуляции», произойдет ее поиск во внешней среде.
Вывод к внешней переменной происходит за счет alert (username) за счет описанных принципов. В коде все это имеет интерпретацию поиска во внешней области видимости, за пределами функции.
Важные выводы и внешние переменные
В результате изученной информации можно сделать следующие заключения:
- Каждая «операция» будет при создании получать ссылку [[Scope]] на объект с переменными, в контексте которого она появилась.
- После запуска создается новый объект с «хранилищами» LexicalEnvironment. Он получит ссылку на внешний объект переменных из [[Scope]].
- Во время поиска переменных оный произойдет сначала внутри текущего объекта переменных, лишь после – по соответствующей ссылке.
На первых порах подобная информация может показаться сложной. Но она позволяет разобраться в более важных для программиста вещах.
Функции – кратко о самом важном
Замыкания в JS напрямую связаны с функциями. В них объявляются не только локальные переменные, но и другие «операции». А значит, целесообразно говорить о вложенности. Такой прием позволяет намного проще реализовывать некоторые программные коды.
Вложенные функции – как применять
Вот пример кода:
Он поможет разобраться с эффективным применением вложенных функций при замыканиях. Он предусматривает такие моменты:
- getFullName() – вспомогательная «операция».
- Вложенные «манипуляции» получают [[Scope]] вместе с глобальными:
- Соответствующий прием обеспечит получение getFullName() снаружи lastName и firstName.
Стоит обратить внимание на то, что при отсутствии рассматриваемой переменной во внешнем объекте, она будет искаться в «более внешнем» через [[Scope]] внешней «операции».
Указанная ранее информация ведет к тому, что последний пример кода (выше) будет успешно работать.
Возврат – return function
Но есть и более сложные ситуации. Пример – внутри одной «операции» будет создана другая, после чего система выведет оную в виде итогового результата. Такая манипуляция рассматривается при создании интерфейсов как «обычная». В виде «операции» может выступать обработчик действий клиента.
Рассматриваемый случай предусматривает счетчик-«манипуляцию», которая будет считывать собственные вызовы. Он возвращает текущее количество оных.
Выше makeCounter будет создавать счетчик. Результат – два независимых «элемента» — counter и counter2. Каждый из них незаметно отвечает за сохранение количество вызовов в переменной внешнего типа currentCount. Ей обладает каждый счетчик.
Что здесь происходит
Если рассмотреть кодификацию подробно, будет происходить следующее:
- В строчке (*) запустится makeCounter, создастся LexicalEnvironment для переменных нынешнего вызова. У «операции» лишь одна переменная под названием var currentCount. Она выступит свойством соответствующего объекта. Сначала инициализируется в undefined, после обработки ей будет присвоено значение 1.
- Когда будет обрабатываться makeCounter, произойдет создание «операции» в строчке (**), что приведет к присваиванию внутреннего свойства [[Scope]]. Она получит ссылку на нынешнее LexicalEnvironment.
- Вызов «МейкКантер» завершается. «Манипуляция» (**) будет возвращена и сохранена во внешней переменной counter (*).
- В counter запишется «операция»:
- Counter, возвращенная из «МейкКантер» будет помнить об окружении, в котором возникла. Это происходит за счет «Скоп».
- При вызове counter неизвестно, что произойдет, так как она содержит всего одну строку – с return. Собственные объект переменных (LE) окажется пустым.
- За счет свойства [[Scope]] указывается внешнее окружение. Для увеличения и возврата currentCount интерпретатор будет искать в нынешнем объекте переменных LE. Оные не обнаружатся.
- Осуществляется переход на внешний объект, где «предмет розыска» будет обнаружен, изменен и возвращен:
Переменные во внешних областях видимости удается не только считывать, но и корректировать.
Приведенный пример в console продемонстрировал независимость взаимного характера имеющихся счетчиков. Это из-за того, что при активации makeCounter будет создаваться собственный объект переменных LE с «личными» свойствами currentCount. Именно на него очередной счетчик будет получать ссылку [[Scope]].
Как быстро освоить тему
Изучая замыкание, можно легко запутаться. Это приводит к тому, что разобраться с выбранным направлением оказывается весьма проблематично. Но выход есть всегда – это дистанционные тематические курсы.
Срок обучения – от нескольких месяцев до года. Предложения для начинающих и опытных разработчиков. Гарантирована практика, а также контакты с опытными разработчиками. Можно в кратчайшие сроки выучить несколько языков программирования и углубиться в них. В конце обучения выдается электронный сертификат, подтверждающий знания.