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

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

Если дать более техническое определение, то оно звучит так:

Область видимости переменных или просто “Область видимости” (англ. 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 будет браться из глобальной области видимости. (О том, как именно происходит сам поиск нужных переменных, будет разбираться в следующей части).

Это называется “затенение переменных”, когда текущая переменная скрывает значение переменной объявленной в родительской области видимости. Вообще, чтобы избежать путаницы при командной разработке и неясности в том, в каких переменных что хранится, лучше не применять затенение и использовать разные имена переменных.

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

Обнаружили ошибку или хотите добавить что-то своё в документацию? Отредактируйте эту страницу на GitHub!

Оставить комментарий