Область видимости переменной в Javascript (variable scope) - ликбез

December 22, 2009, revised March 21, 2011 JavaScript Ликбез

Для меня одним из самых проблемных мест Javascript было управление переменными. Излагаю простым русским языком.

Обновление 21/03/2011: статья эта писалась давно, и ряд описанных в ней вещей оказались если не ошибочными, то по крайней мере неправильно описанными. Теперь ошибочных вещей стало меньше.

Вообще могу порекомендовать почитать раздел, посвященный области видимости, в Саду Javascript, да и весь Сад тоже.

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

Область видимости переменной в Javascript ограничивается ближайшей функцией, в которой она определена.

Глобальных переменных (по крайней мере, в браузере) не бывает, поскольку все скрипты выполняются в контексте window.

<script>
  alert(location); // сообщит window.location
</script>

Объявление переменных

Без использования слова var используется существующая переменная.

Если таковой нет, используется свойство объекта window, то есть “как бы глобальная” переменная.

function foo() {
  a = 2;
  b = 3;
  return a+b;
}
alert(a); // undefined
a = 'очень важное значение';
alert(a); // очень важное значение
foo();
alert(a); // 2

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

Явно объявлять переменные можно и нужно ключевым словом var

var a = 2;

Такая строка всегда создает новую локальную переменную. Если объявление происходит вне функций, создастся свойста объекта window, что вполне логично.

function foo() {
  var a = 2;
  var b = 3;
  return a+b;
}
alert(a); // undefined
var a = 'очень важное значение';
alert(a); // очень важное значение
foo();
alert(a); // очень важное значение

Как объявить “глобальную” переменную из функции? Как обратиться к “глобальной” переменной, если есть локальная с таким же именем? Очень просто – нужно обратиться к ней как к свойству window:

function foo() {
  var location = 'location';
  alert(location); // вернет 'location'
  alert(window.location); // вернет window.location
  window.a = 'переменная из функции';
}
alert(a); // undefined
foo();
alert(a); // переменная из функции

Наследование области видимости

Функции в Javascript представляют собой замыкания. Что это значит? Что функции запоминают ту область видимости, в которой они были созданы.

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

function alertOnTimeout(message, timeout) {
  return setTimeout(function() { 
    // message будет доступен в безымянной функции, переданной таймауту
    alert(message); 
  }, timeout);
}

Передача кода по старинке – строкой, которая прогоняется через eval() – не попадает под это правило, код исполняется в той области видимости, где и определен.

Поскольку объекты в Javascript - это тоже типа функции, то свойство объекта определяется точно так же, как и переменная.

function myObject() {
  var property = 0;
  // Cамо собой, property будет доступно только внутри объекта.
}

this

А что this? А то, что эта переменная автоматически появляется в методах объектов и затирает значение this из предыдущей области видимости. Решение простое – переприсваивать ее значение другой переменной.

$('div.with-links').click(function() {
  var theDiv = this; //сохраняем значение this
  $(this).find('a').click(function() {
    alert($(this).attr('href')); // this - это ссылка
    theDiv.remove(); // а theDiv - это все еще дивак
  });
});

Отдельно замечу, что при оборачивании какой-то поведенческой логики в объект надо помнить, что в создаваемых DOM-событиях значение this самого объекта теряется.

function myObject() {
  var _this = this; // сохраняем ссылку на родительский объект
  var linkRemoved = false;

  $('a').click(function() {
    $(this).remove(); // this - это объект ссылки
    _this.linkRemoved = true; // _this - это родительский объект
  });
}

Buy Me a Coffee at ko-fi.com