Перевод 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, но разумное использование замыканий может помочь сделать код более приятным, чистым и читабельным. Сам принцип возвращения функций подобно возвращению чисел и строковых значений предоставляет вам больше свободы для маневра.
Автор: NIX_Solutions