22

Каков объем видимости переменных в JavaScript?

14

Заголовок: Область видимости переменных в JavaScript

Описание проблемы:

Какова область видимости переменных в JavaScript? Имеют ли они одинаковую область видимости внутри функции по сравнению снаружи? Или это не имеет значения? Кроме того, где хранятся переменные, если они определены глобально?

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

5 ответ(ов)

2

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

Элемент в цепочке областей видимости можно рассматривать как структуру данных, похожую на Map, с указателем на родительскую область.

При разрешении переменной JavaScript начинает искать сначала в самой внутренней области видимости и движется наружу.

0
  1. В JavaScript есть глобальная область видимости, область видимости функции, а также области видимости, созданные конструкциями with и catch. В общем и целом, в JavaScript нет области блочного уровня для переменных — конструкции with и catch вводят имена в свои блоки.

  2. Области видимости вложены функциями вплоть до глобальной области видимости.

  3. Свойства объектов разрешаются путем прохода по цепочке прототипов. Конструкция with добавляет имена свойств объекта в лексическую область видимости, определяемую блоком with.

EDIT: ECMAScript 6 (Harmony) предполагает поддержку let, и я знаю, что Chrome позволяет использовать «флаг harmony», так что, возможно, он действительно поддерживает это.

Ключевое слово let обеспечит поддержку блочной области видимости, но вы должны использовать это ключевое слово, чтобы это произошло.

EDIT: Основываясь на том, что Бенжамин отметил в комментариях о конструкциях with и catch, я отредактировал пост и добавил больше информации. Оба оператора with и catch вводят переменные в соответствующие им блоки, и это и есть блочная область видимости. Эти переменные являются ссылками на свойства объектов, переданных в них.

// chrome (v8)

var a = { 'test1': 'test1val' }
test1   // ошибка: не определено
with (a) { var test1 = 'replaced' }
test1   // undefined
a       // a.test1 = 'replaced'

EDIT: Уточняющий пример:

test1 находится в области видимости блока with, но ссылается на a.test1. Объявление var test1 создает новую переменную test1 в верхнем лексическом контексте (функция или глобальная область), если она не является свойством объекта a — а она ей является.

Ой! Будьте осторожны с использованием with — так же как var не имеет эффекта, если переменная уже определена в функции, это также не имеет эффекта в отношении имен, импортированных из объекта! Немного предупреждения о том, что имя уже определено, сделало бы это гораздо безопаснее. Лично я никогда не буду использовать with из-за этого.

0

JavaScript имеет только два типа области видимости:

  1. Глобальная область видимости: Глобальная область видимости — это область видимости на уровне окна. В ней переменная доступна на протяжении всего приложения.

  2. Функциональная область видимости: Переменные, объявленные внутри функции с помощью ключевого слова var, имеют функциональную область видимости.

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

a = "global";
function outer() { 
    b = "local";
    console.log(a + b); //"globallocal"
}
outer();

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

  1. Уровень окна — переменные a и функция outer находятся на верхнем уровне в цепочке области видимости.
  2. Когда вызывается функция outer, создается новый объект области видимости переменной (который также включает функции в цепочку), и в него добавляется переменная b.

Теперь, когда требуется переменная a, JavaScript сначала ищет ближайший объект области видимости, и если переменной там нет, то переходит к следующему объекту в цепочке области видимости, которым в данном случае является уровень окна.

0

Вот перевод на русский в стиле ответа на StackOverflow:


Запустите следующий код, чтобы понять, как работает область видимости в JavaScript:

Name = 'глобальные данные';
document.Name = 'данные текущего документа';
(function(window, document) {
    var Name = 'локальные данные';
    var myObj = {
        Name: 'данные объекта',
        f: function() {
            alert(this.Name);
        }
    };

    myObj.newFun = function() {
        alert(this.Name);
    };

    function testFun() {
        alert("Область видимости окна: " + window.Name + 
              "\nЛокальная область: " + Name + 
              "\nОбласть объекта: " + this.Name + 
              "\nОбласть текущего документа: " + document.Name
             );
    }

    testFun.call(myObj);
})(window, document);

Этот код показывает различия в областях видимости переменных. Когда вы вызываете метод testFun с this, установленным в myObj, this.Name будет ссылаться на Name из myObj, а не на локальную переменную Name или глобальные данные. Попробуйте запустить этот код, чтобы увидеть, как работают области видимости и привязка this.

0

В JavaScript существуют почти два типа области видимости:

  • Область видимости каждой декларации var ассоциирована с самой ближайшей к ней функцией.
  • Если для декларации var нет окружающей функции, то такая декларация попадает в глобальную область видимости.

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

var i = 10, v = 10;
for (var i = 0; i < 5; i++) { var v = 5; }
console.log(i, v);
// вывод: 5 5

Если же использовать функции, то:

var i = 10, v = 10;
$.each([0, 1, 2, 3, 4], function(i) { var v = 5; });
console.log(i, v);
// вывод: 10 10

В первом примере не было блочной области видимости, и поэтому изначально объявленные переменные были перезаписаны. Во втором примере появилась новая область видимости благодаря функции, и изначально объявленные переменные были лишь ПОДТЕНЕНЫ, но не перезаписаны.

Вот что нужно знать о областях видимости в JavaScript:

  • try/catch создаёт новую область видимости только для переменной исключения, для других переменных новой области видимости не возникает.
  • with тоже является исключением, но использовать его крайне не рекомендуется (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with).

Таким образом, область видимости в JavaScript на самом деле довольно проста, хотя и не всегда интуитивно понятна. Несколько моментов, на которые стоит обратить внимание:

  • Декларации var поднимаются (hoisting) к началу области видимости. Это значит, что независимо от того, где происходит декларация var, для компилятора она будет восприниматься как находящаяся в начале.
  • Множественные декларации var в одной области видимости объединяются.

Поэтому данный код:

var i = 1;
function abc() {
  i = 2;
  var i = 3;
}
console.log(i);     // вывод: 1

эквивалентен:

var i = 1;
function abc() {
  var i;     // декларация var перемещена к началу области видимости
  i = 2;
  i = 3;     // присвоение остается на месте
}
console.log(i);

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

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь