Область видимости. Основы
Одной из самых основных идей практически всех языков программирования является возможность сохранять значения в переменных, а позже извлекать или менять эти значения.
Правила добавление переменных в нашу программу так, чтобы потом можно было их найти и получить их значение, должны определяться четкими границами. Эти границы определяют, какие переменные будут нам доступны в определенных местах программы, и процесс поиска переменных по их идентификатору (имени).
Если дать более техническое определение, то оно звучит так:
Область видимости переменных или просто “Область видимости” (англ. variable scope или просто scope) — это такая область программы, в пределах которой установлена связь между некоторой переменной и её идентификатором (именем), по которому можно получить значение этой переменной. За пределами области видимости тот же самый идентификатор может быть связан с другой переменной, либо быть свободным (вообще не связанным ни с какой из переменных).
Для понимания этой концепции можно привести следующую аналогию. Представим человека, которому была назначена деловая встреча в некотором бизнес-центре, где он раньше не был. Он заходит в нужный кабинет и так как там еще никого нет, он решает узнать время, для этого ему необходимы часы. Этот человек начинает искать часы, обнаруживает настольные часы и узнаёт по ним время.
В данной аналогии:
- область видимости (scope) — это кабинет;
- имя переменной, по которой происходит поиск нужного предмета, это “часы” (обозначим как clock);
- в данной области видимости, с этим идентификатором (“часы”) ассоциируются настольные часы. Здесь стоит вспомнить, что переменные, это своего рода контейнеры (области памяти), для хранения необходимых значений. Именно настольные часы в рамках кабинета выступают неким контейнером для хранения времени.
- значение этой переменной обозначим как “время на настольных часах”.
Поэтому, в данном кабинете переменная, которая представляет собой “настольные часы”, в JavaScript можно объявить как
let clock = "время на настольных часах";
Теперь допустим, что человек в кабинете не нашел абсолютно никаких часов, другими словами в этой области видимости нет никаких переменных (ни настольных часов, ни каких-либо других), которые были бы связаны с идентификатором “часы” (clock
). И в таком случае, в JavaScript запрос получения значения переменной с именем clock вызовет ошибку: Uncaught ReferenceError: clock is not defined
. Которая сообщает о том, что в данной области видимости переменная, связанная с идентификатором clock
, не определена.
Разовьем аналогию дальше и представим, что хоть часов в кабинете и нет, этот человек знает, что в холле здания есть настенные часы. Он знает об этом, так как проходя в кабинет, отметил их наличие. И поэтому, он может получить нужное значение (время) из своего окружения — “выглянуть” из кабинета и посмотреть время на настенных часах.
В этой аналогии здание — это еще одна область видимости, которая также является окружением для области видимости “кабинет”. В данном здании идентификатор “часы” (clock
) связан с переменной “настенные часы в холле”. И объявление этой переменной будет следующим:
let clock = "время на настенных часах в холле";
Мы можем продолжить эту аналогию. Представим, что и в здании нет часов, но они есть на улице. Поэтому, даже находясь в кабинете, он может обратиться к своему окружению — выглянуть на улицу и посмотреть время на уличных часах.
В JavaScript области видимости ограничиваются функциями (функциональная область видимости) или блоками инструкций (блочная область видимости).
Поэтому, пример с часами на JavaScript можно записать так:
function street() {
//scope: street
let clock = "время на уличных часах";
function building() {
//scope: building
function office() {
//scope: office
alert(clock); //покажет "время на уличных часах"
}
}
}
Также необходимо отметить, что переменные, объявленные внутри функции, недоступны снаружи, то есть даже если часы есть в кабинете, а человек находится в здании или на улице, где нет никаких часов, то обратится к часам в кабинете он не сможет. Более того, он в принципе не имеет никакого представления о содержимом кабинета, пока туда не зайдет.
function street() {
//scope: street
function building() {
//scope: building
alert(clock); // выполнение прервётся с ошибкой: Uncaught ReferenceError: clock is not defined
function office() {
//scope: office
let clock = "время на настольных часах";
alert(clock); // покажет "время на настольных часах"
}
}
}
Если нам необходимо, чтобы переменная была доступна отовсюду, то её необходимо объявить вне всех функций, то есть глобально.
Глобальная область видимости - это та, у которой нет внешней области видимости (внешнего окружения). Она является отправной точкой нашей программы и самой внешней областью видимости для всех других, которые в неё вложены.
В нашей аналогии, пусть это будет вселенная, где есть часы, которые можно увидеть из любой точки.
Переменные называются глобальными, если они доступны из любой точки программы. То есть те, которые объявлены в глобальной, самой внешней области видимости. Переменные, которые объявлены внутри функции и недоступны снаружи, называются локальными переменными. Область видимости, ограниченная функцией или блоком, это локальная область видимости.
Глобальная область видимости не вложена ни в какие другие области и находится вне всех функций, являясь родительской для всех других вложенных в неё областей видимости. И для объявления глобальной переменной clock наш код можно переписать так:
//global scope: window
let clock = "время на часах, что доступны отовсюду";
function street() {
//scope: street
function building() {
//scope: building
function office() {
//scope: office
alert(clock); //покажет "время на часах, что доступны отовсюду"
}
}
}
В случае, если одна и та же переменная объявлена в нескольких вложенных областях видимости, её значение будет браться из текущей области видимости, где она запрашивается. Например:
//global scope: window
let clock = "время на часах, что доступны отовсюду";
function street() {
//scope: street
alert(clock); //покажет "время на часах, что доступны отовсюду"
function building() {
//scope: building
let clock = "время на настенных часах в холле";
alert(clock); //покажет "время на настенных часах в холле"
function office() {
//scope: office
let clock = "время на настольные часах";
alert(clock); //покажет "время на настольные часах"
}
}
}
Несмотря на то, что переменная clock
объявлена и глобально, и в функциях building
и office
, её значение будет браться из той области видимости, где оно непосредственно запрашивалось через alert(clock)
.
Если в текущей области переменная не определена, как в функции street
, то значение возьмется из ближайшего окружения (родительской области видимости), в котором определена необходимая переменная. В данном случае, для области видимости street
, значение clock
будет браться из глобальной области видимости. (О том, как именно происходит сам поиск нужных переменных, будет разбираться в следующей части).
Это называется “затенение переменных”, когда текущая переменная скрывает значение переменной объявленной в родительской области видимости. Вообще, чтобы избежать путаницы при командной разработке и неясности в том, в каких переменных что хранится, лучше не применять затенение и использовать разные имена переменных.
Пока в примере мы разбирали локальные области, которые были ограничены функциями. Но также область видимости может быть ограничена блоком инструкций.
Оставить комментарий