Перевод JavaScript Environment, Lexical Scope and Closures.

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

Аналогичным образом устроена программа. То, что вы создаете снаружи — внешние функции, условные операторы, циклы и другие блоки — представляет собой внешнюю, глобальную среду.



Постоянная age, функция multiplier и переменная result находятся во внешней среде. Эти компоненты обладают глобальной областью видимости. Область видимости — это область, в которой доступен компонент.

В данном случае x является константой внутри функции multiplier. Поскольку она находится внутри блока кода, она представляет собой локальную константу, а не глобальную. Она видна только внутри функции, но не снаружи — её область видимости локальна.

Функция multiplier обладает еще одним компонентом из локальной области видимости — это аргумент num. Определить его сложнее, чем постоянную или переменную, но он ведет себя примерно как локальная переменная.

У нас нет внешнего доступа к x — его как будто не существует:



console.log вызвал x в глобальной среде, в которой тот не определен. В результате мы получили ReferenceError.

Мы можем задать x глобально:



У нас появился глобальный x с известным значением, но локальный x в функции multiplier по-прежнему виден только изнутри. Эти x никак не связаны — они находятся в разных средах. Несмотря на одинаковое название, они не смешиваются.

Любой блок кода в фигурных скобках становится локальной средой. Вот пример использования if:



То же происходит с циклами while и for.

Итак, локальный — значит, невидимый снаружи. То есть, глобальный — это видимый повсюду, даже внутри объектов? Да!



Глобальная переменная a изменилась внутри функции changer. Функция начинает действовать только при вызове, а не при определении, поэтому изначально a=0, а при вызове changer принимает значение 1.

Легко поддаться соблазну и поместить всё в глобальную область видимости, забыв о всех сложностях отдельных сред — но это ужасная идея. Глобальные переменные делают ваш код крайне хрупким, любой элемент может сломать любой другой в любой момент. Поэтому не пользуйтесь глобальной областью видимости и держите всё на местах.

Часть II. Лексическая область видимости


Взгляните на эту программу:



Функция multiplier возвращает результаты умножения a и b. a задана внутри, а b нет.

При попытке произвести операцию умножения a*b, JavaScript ищет значения a и b. Он начинает поиск внутри, а затем выходит наружу, изучая одну область за другой, пока не находит то, что искал, или не понимает, что это невозможно найти.

Следовательно, в этом примере JavaScript начинает поиски a внутри локальной области — внутри функции multiplier. Он тут же находит значение и переходит к b. b в локальной среде он не найдет, поэтому выходит за её пределы. Там он узнаёт, что b равняется 10. Значит, a*b превращается в 5*10, а затем в 50.

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

Обратите внимание, что a=7 никак не влияет на результат вычислений: значение a нашлось внутри, поэтому внешняя a роли не играет.

Это называется лексической областью видимости. Область видимости любого компонента определяется местоположением этого компонента в коде, а у вложенных блоков имеется доступ к внешним областям.

Часть III. Замыкания


Среды и области видимости поддерживаются большинством языков программирования, и этот механизм позволяет вводить замыкания. Замыкание по своей сути — функция, которая «запоминает» внешние сущности, использованные внутри.

Прежде чем продолжить, давайте вспомним, как создаются и используются функции:



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

Важно помнить, что это отдельные компоненты. Первый компонент — константа под названием f. Её значением может быть число или строковое значение. В данном случае значением является функция.

В прошлых уроках мы приводили аналогию: константы похожи на листы бумаги с названием на одной стороне и значением на другой. Таким образом, f — лист бумаги, на одной стороне которого написано f, а на другой — описание запускаемой функции.

При вызове этой функции:



Создается новую «коробку» на основе описания с листа бумаги.

Вернемся к замыканиям. Вот пример кода.



Функция createPrinter создает константу name, а затем функцию под названием printName. Обе локальны по отношению к функции createPrinter и доступны только внутри неё.

У самой printName локальных компонентов нет, но есть доступ к области видимости, где она создается, и к внешней среде, где постоянной присваивается name.

Функция createPrinter возвращает функцию printName. Напоминаем, что определения функций представляют собой описание запущенных функций, это просто элементы данных, как числа или цепочки. Следовательно, мы может вернуть определение функции так же, как возвращаем числа.

Во внешней области мы создаем константу myPrinter и задаем возвращаемое ей значение как createPrinter. Возвращается функция, так что myPrinter также становится функцией. При её вызове на экране появится King.

Вот что забавно: константа name была создана внутри функции createPrinter. Функция была вызвана и выполнена. Как известно, когда функция заканчивает работу, она прекращает существование. Волшебная коробка пропадает вместе со всем содержимым.

Но он вернул другую функцию, которая каким-то образом запомнила константу name. Таким образом, при вызове myPrinter мы получили King — значение, которое функция помнит, несмотря на то, что эта область больше не существует.

Функция, которую возвращает createPrinter, называется замыканием. Замыкание — комбинация функции и среды, в которой она была определена. Функция «замкнула» в себе определенную информацию, полученную в области видимости.

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

Комментарии (8)


  1. rudinandrey
    23.07.2019 11:28

    Прошу прощения, не по теме, а что за шрифт? мне очень понравился, я тоже себе такой хочу в VSCode. Заранее благодарен. Я сейчас статью прочитаю, просто давно очень такой шрифт уже где то такой видел, но не получилось найти.


    1. NIX_Solutions Автор
      23.07.2019 12:21

      Про шрифт не подскажу, в оригинале было уже в таком виде :)


    1. shaukote
      23.07.2019 18:17

      Есть очень похожий Operator Mono Font, но в нём нет лигатур, насколько я помню.


      1. your_eyes_lie
        23.07.2019 21:45

        Можно и с лигатурами — github.com/kiliman/operator-mono-lig


      1. rudinandrey
        23.07.2019 22:58

        вроде похож, спасибо.


        1. shaukote
          23.07.2019 23:50

          На здоровье. :)
          К сожалению, основная проблема Operator Mono (для меня) — очень слабая кириллица, сильно контрастирующая по внешнему виду с латиницей.


    1. dayfuaim
      24.07.2019 23:47

      Это "Operator Mono". :)


    1. BBmike
      24.07.2019 23:47

      это Operator