Области видимости
Время чтения: 13 мин
Обновлено 29 ноября 2021
Кратко
Мы используем переменные, чтобы хранить в них временные значения, а потом в нужный момент получать к ним доступ.
Но не все переменные в нашем коде одинаково доступны. Доступна ли переменная и как получить к ней доступ, определяют области видимости.
Область видимости — это часть программы, в которой мы можем обратиться к переменной, функции или объекту. Этой частью может быть функция, блок или вся программа в целом — то есть, мы всегда находимся как минимум в одной области видимости.
Области видимости можно представить как коробки, в которые мы кладём переменные. Переменные, которые лежат в одной коробке, могут общаться друг с другом.
Переменные также могут получить доступ к переменным из коробки, в которую вложена их коробка.
Области видимости помогают скрывать переменные от нежелательного доступа, управлять побочными эффектами и разбивать код на смысловые блоки.
Но прежде чем мы рассмотрим, как их использовать, разберёмся с тем, какие области видимости в JS вообще есть.
Глобальная область видимости
Глобальная область видимости — это самая внешняя коробка из всех. Когда мы «просто объявляем переменную», вне функций, вне модулей, то эта переменная попадает в глобальную область видимости.
Переменная в примере сейчас находится в глобальной области видимости. Это значит, что она будет доступна откуда угодно внутри модуля:
Переменные в глобальной области видимости называются глобальными переменными и доступны всем.
Глобальный объект window — это объект, который даёт доступ к Web API браузера. window рекурсивно «содержит сам себя», потому что все глобальные объекты находятся в window :
Мы также можем определить глобальные переменные сами. Например, если в консоли браузера мы создадим какую-то переменную, а потом попробуем получить к ней доступ через window :
Блочная область видимости
Блочная область видимости ограничена программным блоком, обозначенным при помощи < и >. Простейший пример такой области — это выражение внутри скобок:
Переменная b скрыта внутри области видимости блока внутри скобок и доступна только внутри этого блока, но не снаружи.
В примере выше в case нам надо выполнить несколько строчек. Удобно обернуть все операции в блок при помощи фигурных скобок — тогда все переменные и операции будут ограничены этим блоком, то есть, блочной областью видимости.
Функциональная область видимости
Функциональная область видимости — это область видимости в пределах тела функции. Можно сказать, что она ограничена < и >функции.
Функциональная область видимости — очень мощный инструмент для разделения кода. Во-первых, используя её, мы можем не опасаться за «пересечение имён» переменных.
В одной области видимости объявить дважды let или const нельзя:
Но функции создают собственные области видимости, которые не пересекаются, поэтому в этом случае ошибки не будет:
Так как области видимости у функций не пересекаются и не связаны, первая функция не может обратиться к «внутренностям» соседней или вложенной функции, внутренности соседней функции скрыты в её области видимости и недоступны вне её:
То же и с дочерними областями:
Функциям доступны лишь переменные в её собственной области видимости (всё, что внутри её тела) и в родительских областях:
Такое поведение, когда переменные родительских областей становятся доступны в дочерних, называется наследованием областей видимости.
Такой особенный доступ к локальным переменным родительской функции часто называют лексической областью видимости.
Сокрытие «внутренностей» позволяет создавать независимые друг от друга блоки кода. Это, например, полезно, когда мы хотим запустить какой-то модуль в браузере с уверенностью, что он никак не повлияет на другой код.
Изоляция модулей с помощью IIFE
Immediately Invoked Function Expression, IIFE — это функция, которая выполняется сразу же после того, как была определена.
Записывается IIFE так:
Разберём по частям.
Это обычная функция, которая описывается и ведёт себя по всем правилам функций в JS.
Скобки превращают функцию в выражение, которое можно вызвать. То есть, если до этого шага мы функцию объявили, то на этом шаге мы приготовили её к мгновенному вызову.
Последняя пара скобок вызывает выражение, то есть вызывает функцию, которую мы создали на 1-м шаге и подготовили на 2-м.
Так как функция внутри скобок — это обычная функция, она точно так же создаёт внутри себя область видимости, доступ к которой есть только у неё. То есть всё, что внутри функции, остаётся внутри.
При помощи IIFE мы можем использовать одинаковые названия переменных, не боясь, что они случайно перезапишут значения переменных из чужих модулей, если мы не контролируем кодовую базу полностью сами.
Никаких конфликтов имён!
Функции внутри функций и замыкания
Как мы видели выше, у дочерней функции есть доступ к области видимости родительской функции:
То есть мы смогли «обойти» область видимости? Не совсем.
Грубо говоря, мы создали функцию, которая даёт нам читать переменные, но не изменять их. Это полезно, если мы хотим дать ограниченный доступ к внутренностям модуля.
Допустим, мы хотим сделать счётчик, который можно увеличивать и уменьшать только на единицу:
Такое контролируемое сокрытие доступа с помощью области видимости называется замыканием.
Замыкания удобны тем, что каждый новый вызов создаёт отдельную область, в которой значения абсолютно независимы друг от друга:
Состояния обоих счётчиков друг от друга не зависят, хотя они создаются одной и той же функцией.
«Поднятие» переменных (hoisting)
Выше, когда мы экспериментировали с window и глобальными переменными, мы обговорили, что запись:
Для начала посмотрим на такой код:
Чтобы понять, почему доступ к переменной a не вызывал ошибки, разберёмся, как работает var и объявление переменных.
Так как переменная a не была объявлена, то JavaScript сам решил, где объявлять переменную, и «поднял» объявление наверх. Получился вот такой код:
Более того, переменные «поднимаются» и внутри блоков и функций:
Потому что на самом деле код превращается вот в это:
Проблема
На самом деле это большая проблема, потому что о «поднятии» легко забыть и случайно перезаписать значение переменной в ненужный момент.
Как бороться
Но механизм «поднятия» даже внутри блока достаточно непредсказуемый. Идеально было бы, если бы переменные объявлялись и инициализировались там, где это указано в коде.
C let и const — нет:
На практике
Саша Беспоясов
Используйте IIFE для изолированных модулей или для эмуляции top-level await там, где его ещё нет:
Используйте замыкания, чтобы предоставлять контролируемый доступ к внутренним значениям модуля или чтобы разграничивать области видимости и имён:
JavaScript: область видимости простыми словами
Доброго времени суток, друзья!
Область видимости — важная концепция, определяющая доступность переменных. Данная концепция лежит в основе замыканий, разделяя переменные на глобальные и локальные.
В этой статье я постараюсь простыми словами объяснить, что такое область видимости в JavaScript.
1. Область видимости
Перед тем, как погружаться в детали, связанные с областью видимости, рассмотрим небольшой пример.
Допустим, мы определили переменную:
Мы легко можем вывести ее значение в консоль. Это понятно.
Теперь поместим объявление переменной message в блок if:
На этот раз при попытке доступа к переменной выбрасывается исключение ReferenceError: message is not defined.
Почему это произошло?
Потому что блок if создал область видимости для переменной message. И message доступна только внутри этой области.
Таким образом, доступность переменных ограничена областью видимости, в которой они определены.
Итак, область видимости — это зона доступности переменных.
2. Блочная область видимости
Блок кода в JavaScript определяет область видимости переменных, объявленных с помощью ключевых слов const и let:
Первый console.log() благополучно выводит значение переменной message в консоль, поскольку доступ к этой переменной осуществляется в той области видимости, в которой она определена.
Однако вызов второго console.log() приводит к возникновению ошибки, поскольку переменная message недоступна во внешней по отношению к ней области видимости: в текущем контексте message не существует.
В инструкциях if, for, while также создается блочная область видимости.
Переменные color и message существуют только внутри блока for.
Тоже самое справедливо для инструкции while:
message, определенная в while, доступна только внутри данного цикла.
В JavaScript вы можете создавать самостоятельные блоки кода. Они также определяют собственную область видимости:
2.1. var не имеет блочной области видимости
Как мы видели в предыдущих примерах, блок кода создает область видимости для переменных, объявленных с помощью ключевых слов const и let. Однако это не работает для переменных, объявленных с помощью ключевого слова var.
Переменная count, как и ожидалось, доступна внутри блока if. Однако, она доступна и за пределами данного блока!
Дело в том, что блок кода не создает области видимости для переменных, объявленных с помощью ключевого слова var. Но это делает функция.
3. Область видимости функции
Функции в JavaScript создают область видимости для всех переменных, независимо от того, с помощью какого ключевого слова они объявлены (var, const или let).
Функция run() создает область видимости. Переменная message доступна внутри функции, но недоступна снаружи.
Аналогичным образом функция создает область видимости для переменных, объявленных с помощью const и let, и даже для других функций и функциональных выражений:
4. Область видимости модуля
Модули ES6 также создают область видимости для переменных, функций и классов.
Модуль circle создает константу pi (для внутреннего использования):
Переменная pi объявляется внутри модуля circle и не экспортируется из него.
Затем модуль circle импортируется:
Переменная pi недоступна за пределами модуля circle (до тех пор, пока она не будет экспортирована с помощью export).
Модульная область видимости инкапсулирует модули. Это означает, что частные переменные (которые не экспортируются) используются для собственных нужд модуля и защищены от доступа извне.
Таким образом, можно сказать, что область видимости — это механизм инкапсуляции для блоков кода, функций и модулей.
5. Области видимости могут быть вложенными
Интересной особенностью областей видимости является то, что они могут быть вложены одна в другую.
В следующем примере функция run() создает область видимости, а внутри нее блок if создает еще одну область:
Область видимости блока if вложена в область видимости функции run().
Область видимости, находящаяся внутри другой области, называется внутренней областью видимости. В приведенном примере — это область видимости блока if.
Область видимости, содержащая другую область, называется внешней областью видимости. В приведенном примере — это область видимости фукнции run().
Что насчет доступности переменных? Нужно запомнить простое правило:
Переменные из внешней области видимости доступны во внутренней области.
Поэтому переменная message доступна внутри блока if.
6. Глобальная область видимости
Глобальная область видимости является самой внешней областью. Она доступна для любой внутренней или локальной области видимости. В браузере глобальной является область видимости, создаваемая при загрузке JavaScript-файла, указанного в атрибуте src тега script:
Блоки как области видимости. Переменные let и const
Для лучшей стабильности и безопасности работы программы, а также для упрощения самого процесса разработки и дальнейшего сопровождения, код программы следует организовывать по принципу наименьших привилегий. Это значит, что код должен быть организован таким образом, чтобы каждая часть программы, выполняющая определенную задачу, должна иметь доступ к такой информации и ресурсам, которые минимально необходимы для успешного выполнения её рабочей цели.
Именно концепция областей видимости позволяет разграничить доступ определенных частей программы к переменным, методам или другим инструкциям так, чтобы этим частям был доступен только необходимый минимум ресурсов для выполнения их задачи.
До выхода стандарта ES6 функции были наименьшей единицей, ограничивающей области видимости. Но, следуя принципу наименьших привилегий, бывает необходимо разграничить доступ к переменным для определенных частей кода, находящихся в одной и той же функции.
Могут быть следующие причины:
Для этих целей раньше обычно использовались немедленно вызываемые функциональные выражения — IIFE (Immediately Invoked Function Expression) которые представляют собой функции, выполняющиеся сразу же после того, как они были определены. Подробнее о функциях и их видах будет рассказано в следующих частях курса. Сейчас рассмотрим лишь один классический вариант такого функционального выражения:
Поэтому, наш предыдущий пример, до выхода стандарта ES6, выглядел бы так:
Единственными примерами блочной области видимости еще со стандарта ES3 были:
блок catch в конструкции для обработки ошибок try. catch
Как видите, err существует только в блоке catch и выбрасывает ошибку, когда вы пытаетесь сослаться на нее где-либо в другом месте.
Переменные let
Такую пред-итеративную привязку можно записать так:
Важность такой пред-итеративная привязки станет более понятна, когда будет разбираться механизм замыканий.
Этот код довольно легко отрефакторить в менее вложенный:
Но остерегайтесь таких изменений, когда используете переменные блочной области видимости:
Рассмотрим следующий пример:
Переменные const
Для более прозрачного и понятного кода, стоит использовать const для тех переменных, которые впоследствии не будут и не должны меняться. То есть, используйте его как инструмент, отражающий ваше намерение ввести константу, и позволяющий другим разработчиком понять смысл и назначение этой константы.
Повторное объявление переменных в одной области видимости
Для переменных let или const следующий код вызовет ошибки
При объявлении переменных let и const такая проверка, на существование переменных с таким же идентификатором, выполняется среди всех переменных в рамках той же области видимости, даже если они были объявлены через var
Или объявлена как функции
Функции ограниченные блочными областями видимости
Или в случае условной конструкции if-else :
В данном случае вызов функции вызовет ошибку ReferenceError так как вне блока, где она была объявлена, эта функция не определена. Без строгого режима этот код будет работать без ошибок, так как объявление функций уже не будет ограничиваться блоками инструкций.
Для корректной работы предыдущего примера в строгом режиме нужно использовать функциональные выражения:
Функции — Переиспользуемые блоки кода
Другая важная концепция в кодировании — функции — позволяют хранить фрагмент кода, который выполняет одну задачу внутри определённого блока, а затем вызывать этот код всякий раз, когда вам это нужно, используя одну короткую команду, вместо того, чтобы вводить один и тот же код несколько раз.
В этой статье мы рассмотрим фундаментальные концепции функций, такие как базовый синтаксис, способы вызова и их определения, область действия и параметры.
| Предпосылки: | Начальная компьютерная грамотность, основы HTML и CSS, первые шаги JavaScript. |
|---|---|
| Цель: | Понять фундаментальные основы функций языка JavaScript. |
Где можно встретить функции?
В JavaScript, вы везде уведите функции. На самом деле, мы пользовались функциями на протяжении всего курса; только мы не говорили об этом слишком часто. Теперь наступило время, чтобы поговорить о функциях более конкретно и разобрать их синтаксис.
Встроенные функции браузера
В этом курсе мы использовали функции, встроенные в браузер. Каждый раз, когда мы манипулировали текстовой строкой, например:
Или каждый раз, когда мы манипулировали массивом:
Или каждый раз, когда мы генерировали случайное число:
. мы использовали функции!
Примечание: вы можете вставить эти строки в консоль вашего браузера, чтобы посмотреть, как работают эти функции.
Фактически, часть кода, который вы вызываете, когда ссылаетесь на встроенную функцию браузера (воображаемое слово для её запуска или выполнения), не может быть написана на JavaScript — многие из этих функций вызывают части фонового кода браузера, который написан в основном на системных языках низкого уровня, таких как C ++, а не на веб-языках, таких как JavaScript.
Имейте в виду, что некоторые встроенные функции браузера не являются частью основного языка JavaScript — некоторые из них являются частью API браузера, которые основываются на языке по умолчанию, чтобы обеспечить ещё большую функциональность (подробнее см. один из предыдущих разделов этого курса). Более подробно рассмотрим использование API браузера в более позднем модуле курса.
Функции или методы
Пользовательские функции
Нам понадобилась эта функция, потому что встроенная в браузер функция Math.random() генерирует случайное дробное число от 0 до 1. Но мы хотим случайное целое число от 0 до указанного числа.
Вызов функций
Безымянные функции
Вы можете видеть функции, определённые и вызываемые несколькими разными способами. До этого мы создавали функции таким способом:
Но вы также можете создавать функции без имени:
Такая функция называется безымянная функция (или анонимная) — она не имеет имени! Она сама по себе ничего не делает. Обычно такие функции используются вместе с обработчиком событий, например, следующее будет вызывать код внутри функции каждый раз, по нажатию соответствующей кнопки:
В приведённом примере требуется, чтобы на странице был элемент (кнопка), которую нужно нажать. Вы уже видели такую структуру несколько раз на протяжении всего курса, подробнее о ней вы узнаете из следующей статьи.
Вы также можете присвоить к переменной анонимную функцию, например:
Теперь эту функцию можно вызвать, используя:
Фактически такой способ присваивает переменной имя; вы также можете присвоить функцию значением нескольких переменных, например:
Теперь функцию можно вызвать, используя любую из переменных
Но это может ввести в заблуждение, так что не стоит так делать! При создании функций лучше всего придерживаться следующего вида:
Параметры функции
Некоторые функции при их вызове требуют указание параметров — это значения, которые должны быть вставлены в круглые скобки функции, необходимые для корректной работы функции.
Примечание: Параметры иногда называются аргументами, свойствами или атрибутами.
Например встроенная в браузер функция Math.random() не требует параметров. При вызове, она всегда возвращает случайное число от 0 до 1:
Браузерная встроенная функция, работающая со строкой, replace() ожидает два параметра — это подстрока для поиска в основной строке и строка, на которую происходит замена в основной строке:
Примечание: Если необходимо указать несколько параметров, их разделяют запятыми.
Если не указан параметр для символа соединения / разграничения, по умолчанию используется запятая.
Область видимости функции и конфликты
Верхний уровень за пределами всех ваших функций называется глобальной областью (global scope). Значения, определённые в глобальном масштабе, доступны извне в коде.
Например, скажем, у вас есть файл HTML, который вызывается в двух внешних файлах JavaScript, и оба они имеют переменную и определённую функцию, которые используют одно и то же имя:
Примечание: Этот пример можно увидеть в режиме Live на GitHub (см. также исходный код).
Хранение частей вашего кода, заблокированных функциями, позволяет избежать таких проблем и считается наилучшей практикой.
Активное обучение: игра с scope
Давайте посмотрим на реальный пример, демонстрирующий обзор.
Функции внутри функций
Имейте в виду, что вы можете вызывать функцию из любого места, даже если она внутри другой функции. Это часто используется как способ поддержания чистоты кода. Если у вас есть большая сложная функция, её легче понять, если разбить её на несколько подфункций:
Заключение
В этой статье были рассмотрены основные понятия, лежащие в основе функций, позволяющие освоить следующий материал, в котором мы получим практические навыки, и научимся создавать собственные функции.
Введение в JavaScript scope
Введение в JavaScript scope (область видимости функции, область видимости блока).
Область видимости или Scope
Область видимости (scope) определяет видимость или доступность переменной (другого ресурса) в области твоего кода.
Глобальная область видимости или Global Scope
В JavaScript есть только одна глобальная область. Область за пределами всех функций считается глобальной областью, и переменные, определенные в глобальной области, могут быть доступны и изменены в любых других областях.
Локальная область видимости или Local Scope
Переменные, объявленные внутри функций, становятся локальными для функции и рассматриваются в соответствующей локальной области. Каждая функция имеет свою область видимости. Одна и та же переменная может использоваться в разных функциях, поскольку они связаны с соответствующими функциями и не являются взаимно видимыми.
Область видимости функции
Область видимости блока
Лексическая область видимости
Динамическая область видимости
Динамическая область видимости, по понятным причинам, подразумевает, что существует модель, в которой область видимости может определяться динамически во время выполнения, а не статически во время создания. Например:
Динамическая область видимости, напротив, не связана с тем, как и где объявляются функции и области, а связана с тем, откуда они вызываются. Другими словами, цепочка областей видимости основана на стеке вызовов, а не на вложении областей видимости в коде.
Как такое может быть?
Можно подумать что это странно.
Но JavaScript, на самом деле, не имеет динамической области видимости. Он имеет только лексическую область. А вот механизм this подобен динамической области видимости.
И наконец: this заботится о том, как была вызвана функция. Это показывает нам, насколько тесно механизм this связан с идеей динамической области видимости.









