JavaScript - Область видимости переменных

В этой статье познакомимся с таким понятием как область видимости переменных и функций. Т.е. рассмотрим к каким переменным и функциям можно обращаться из одной или другой части кода.

Основные виды контекста

В JavaScript (ECMAScript) область видимости переменной или функции определяется так называемым контекстом выполнения.

В языке JavaScript различают 2 основных вида контекста:

  • Глобальный контекст (контекст объекта window). В этом контексте все переменные и функции создаются как свойства и методы объекта window. Переменные и функции, созданные в этом контексте, будут доступны во всех других функциях.
  • Контекст функции (локальный контекст). Все созданные переменные (с помощью ключевого слова var) и функции будут доступны только внутри этой функции.
// глобальный контекст
var age = 32; //глобальная переменная
// функция sayHello
function sayHello() { //контекст функции
  var myName = "Вася"; //локальная переменная (не будет доступна вне этой функции
  console.log("Привет, " + myName);
}
// функция sayAge 
function sayAge() { //контекст функции
  console.log("Мне, " + age + " года"); //глобальная переменная (доступна в любом контексте) 
}
// вызов функции sayHello
sayHello(); //Привет, Вася
// вызов функции sayAge
sayAge(); //Мне 32 года
console.log("Возраст: " + age); //Возраст: 32
console.log("Моё имя: " + myName); //Ошибка, переменная myName не определена
Основные виды контекста в JavaScript (пример)
Основные виды контекста в JavaScript (пример)

Цепочка областей видимости

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

Представить цепочку областей видимости можно как набор упорядоченных звеньев, в соответствии с которыми ищутся переменные и функции. Цепочка всегда строится, начиная от текущего контекста выполнения. Следующим звеном является родительский контекст, после него идёт родительский родительского и т.д. Заканчивается цепочка всегда глобальным контекстом.

Если переменная или функция найдена, то переход на более высокий уровень не осуществляется.

var myName = "Женя"; //глобальная переменная, т.к. объявлена вне какой-либо функции
function sayHello() { //контекст функции
  var myName1 = "Вася";  // локальная переменная функции sayHello
  console.log("Привет, " + myName);
  console.log("Привет, " + myName1);
  function sayHi() { // контекст функции, которая является дочерней по отношению к sayHello
    var myName2 = "Дима"; // локальная переменная функции sayHi
    console.log("Привет, " + myName);
    console.log("Привет, " + myName1);
    console.log("Привет, " + myName2);
  }
  sayHi();
}
sayHello();
JavaScript - Цепочка областей видимости (пример 1)
JavaScript - Цепочка областей видимости (пример 1)

В контексте выполнения функции sayHi() доступны все 3 переменные:

  • myName2 - расположена в текущем контексте;
  • myName1 - расположена в родительской функции sayHello;
  • myName - расположена в глобальном контексте.

А вот в функции sayHello() доступны только 2 переменные:

  • myName1 - расположена в текущем контексте;
  • myName - расположена в глобальном контекст.

Рассмотрим ещё один пример:

// глобальный контекст
var myName = "Женя"; //глобальная переменная
function sayHello() { // контекст функции
  console.log("Привет, " + myName); // переменная name берётся из глобального контекста 
}
function sayHi() { // контекст функции
   var myName = "Дима"; // локальная переменная функции sayHi
    console.log("Привет, " + myName); // переменная name берётся из текущего контекста
}
// вызов функции sayHi
sayHi();
// вызов функции sayHello
sayHello();
JavaScript - Цепочка областей видимости (пример 2)
JavaScript - Цепочка областей видимости (пример 2)

Функция sayHi() выведет в консоль "Привет, Дима". Это обусловлено тем, что переменная name существует в текущем контексте.

Функция sayHello() отобразит в консоли запись "Привет, Женя". Переменная name найдена в родительском (глобальном) контексте.

Поиск переменной всегда начинается с текущего контекста. Если некоторая переменная найдена в текущем контексте, то дальнейшие действия по её поиску прекращаются. В противном случае, её поиск осуществляется в следующем звене (родительской функции по отношению к текущей). И так далее по цепочке вверх до глобального контекста. Если в каком-то из звеньев цепочки переменная найдена, то переход к следующей цепочки не осуществляется.

JavaScript - Цепочка областей видимости
JavaScript - Цепочка областей видимости

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

myColor = "Зелёный"; //глобальная переменная
// объявление функции getColor
function getColor() {
  var myColor = "Синий"; //локальная переменная
  console.log("Цвет: " + myColor); //вывод значения локальной переменной
  console.log("Цвет: " + window.myColor); //вывод глобальной переменной
}
// вызов функции getColor
getColor();
JavaScript - Получение доступа к глобальной переменной через window
JavaScript - Получение доступа к глобальной переменной через window

Область видимости блока в JavaScript

Блочных областей видимости в JavaScript нет. Т.е. переменная созданная в блоке кода {...} будет видима за его пределами. Это необходимо помнить и учитывать при написании сценариев JavaScript.

Пример:

if (true) {
  var subject = "Математика"; //переменная будет видима и за пределами данного блока
}
console.log(subject); //выведем переменную, объявленную внутри блока

JavaScript - Переменная subject видима за пределами блока

Рассмотрим ещё один пример:

for (var i=1; i<=9;i++) {
  console.log(i*i);
}
console.log(i); //переменная i доступна за пределами блока

JavaScript - К переменной i можно получить доступ за пределами цикла

Ключевое слово var

При создании переменной почти всегда используется ключевое слово var. Использование этого слова означает то, что переменная всегда добавляется в текущий контекст (область видимости). В функции - в контекст этой функции, вне какой-либо функции - в глобальный контекст.

Если Вы не используете строгий режим, то JavaScript позволяет создавать переменные без ключевого слова var. С такими переменными необходимо быть очень осторожными. Это связано с тем, что такая переменная вне зависимости от контекста, в котором она создана, автоматически становится глобальной. А это может привести к непредсказуемым результатам.

Рассмотрим следующий пример:

function sayHello() {
  var myName = "Вася"; // переменная не существует вне этой функции
}
sayHello();
console.log("Привет, " + myName); //строчка выдаст ошибку: myName - не определена.

JavaScript - Ключевое слово var (пример 1)

Если избавиться от ключевого слова var, то тогда переменная будет объявлена как глобальная переменная:

function sayHello() {
  myName = "Вася"; // переменная будет глобальной, несмотря на то, что она объявлена в функции
}
sayHello();
console.log("Привет, " + myName); //строчка вернёт: Привет, Вася.

JavaScript - Ключевое слово var (пример 2)

Некоторые особенности языка JavaScript при работе с переменными и функциями

У языка JavaScript есть одна интересная особенность, которая связана с тем как браузер понимает код, в котором использование переменной идёт до её объявления.

Рассмотрим эту ситуацию на следующем примере:

function sayHello() {
  console.log("Привет, " + myName);
  var myName = "Вася"; // переменная не существует вне этой функции
}
sayHello();

JavaScript - Некоторые особенности языка при работе с переменными

В этом примере вывод значения переменной myName в консоль идёт до её объявления и инициализации.

Как браузер понимает этот код? Браузер объявления переменных всегда поднимает вверх их функциональной области видимости.

Т.е. браузер будет видеть вышепредставленный код следующим образом:

function sayHello() {
  var name;
  console.log("Привет, " + name);
  name = "Вася";
}
sayHello(); 

В результате в консоли отобразится сообщение "Привет, undefined".

Кроме переменных, вверх их функциональной области видимости браузер поднимает ещё функции. Т.е. код JavaScript позволяет вызвать функцию до её объявления.

var name = "Вася";
sayHello(); // вызов функции sayHello
// объявление функции sayHello
function sayHello() {
  console.log(name);
}

JavaScript - Некоторые особенности языка при работе с функциями

Вышеперечисленные особенности языка необходимо знать и по возможности их не использовать в коде. Т.к. это может привести к тому, что код станет более трудночитаемым. В результате это может привести к ошибках.

Поэтому следует придерживаться следующих правил:

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


   JavaScript и jQuery 0    1189 0

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

  1. СТАС # 0
    Как и прежде спасить за статью, но у меня 3 вопроса.
    1. Впервые встречаю функцию в функции, какое-то масло-маслянное. Если можно реально полезный пример.
    2. «Это связано с тем, что такая переменная вне зависимости от контекста, в котором она создана, автоматически становится глобальной.» Как-то кусок кода, по моему, не совсем отображает о том что тут написано. Возможнопросто не самый удачный пример.
    3. «Кроме переменных, вверх их функциональной области видимости браузер поднимает ещё функции. Т.е. код JavaScript позволяет вызвать функцию до её объявления.» Я могу ошибаться, но большинство языков программирования работают по такому же принципу, или здесь что-то новое на что вы хотели обратить внимание?
    1. Александр Мальцев # 0
      1. Ситуации бывают разные. Например, создание функции для внешних ссылок, которое должна выполнять следующее:
      A. Получить все внешние ссылки (функция, возвращающая их в качестве результата)
      B. Добавить ко всем ссылкам элемента span (*). Это действие тоже можно оформить в виде функции и использовать в качестве параметра 1 функцию.
      C. Добавить ко всем внешним функциям обработчик события, который будет отменять стандартное действие и перенаправлять пользователя на определённую страницу, а с неё уже по ссылке.
      Если всё это уже сделать в виде функций, то код становится не только понятнее, но и более прост для дальнейшего совершенствования.
      Бывают и более сложные случаи, и функции-переменные. А в функциях-конструкторах вообще не возможно не использовать функции (в этом контексте они будут методами).

      2. Примеры, возможно, не самые удачные, но простые. Реальные примеры будем рассматривать в других статьях, где будем разбирать реальные задачи.

      3. Ну конечно особенность, а в каких языках это ещё есть? Возможно в тех, которые тоже имеют слабую типизацию.
    2. Женек # 0
      В добавок к предыдущему вопросу. Скажите, а по замыканиям планируете сделать статью?
      1. Александр Мальцев # 0
        Статей по функциям планирую много и про замыкания тоже будет.

      Вы должны авторизоваться, чтобы оставлять комментарии.