Подтвердить что ты не робот

Область действия ловушки JavaScript

Спецификация ECMAScript 5 содержит следующее:

Обычно Лексическая среда связана с некоторыми конкретными синтаксическую структуру кода ECMAScript, такую ​​как FunctionDeclaration, WithStatement или предложение Catch для TryStatement и новый Lexical Окружающая среда создается каждый раз, когда такой код оценивается.

Если мое понимание правильное, тогда, когда в JavaScript создается новая Лексическая среда, вводится новая область, поэтому переменные, объявленные внутри функции, не видны вне этой функции:

function example() {
    var x = 10;
    console.log(x); //10
}
console.log(x); //ReferenceError

Итак, в приведенном выше объявлении функции создается новая Лексическая среда, что означает, что x недоступен в любых внешних Лексических средах, которые могут существовать.

Итак, часть цитаты выше о объявлениях функций, по-видимому, имеет смысл. Однако в нем также указано, что для предложения Catch в заявлении Try создается новая Лексическая среда:

try {
    console.log(y); //ReferenceError so we enter catch
} 
catch(e) {
    var x = 10;
    console.log(x); //10
}
console.log(x); //10 - but why is x in scope?

Итак, как работает область действия блока catch? Есть ли у меня фундаментальное непонимание того, что такое Лексическая среда?

4b9b3361

Ответ 1

Если я понимаю это правильно, то, возможно, это означает, что в вашем коде

try {
    console.log(y); //ReferenceError so we enter catch
} 
catch(e) {
    var x = 10;
    console.log(x); //10
}

e будет существовать только в блоке catch. Попробуйте console.log(e); за пределами блока catch, и он выкинет ReferenceError.

Как и в случае с WithStatement, with ({x: 1, y: 2}) { }, x и y будут существовать только внутри блока.

Но это не значит, что объявления var будут привязаны к ближайшей лексической среде. На самом деле объявления var будут привязаны к среде при вводе контекста выполнения.

10.5. Активация связывания букв: при вводе контекста выполнения он будет искать объявления функций, аргументы и объявления переменных, а затем создавать привязки в контексте выполнения VariableEnvironment.

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

Это означает, что объявления var будут привязаны к ближайшему контексту выполнения.

var x = 1;
(function() {
    x = 5; console.log(x); // 5
    if (false) { var x; }
    x = 9; console.log(x); // 9
})();
console.log(x); // 1

Итак, в приведенном выше коде x = 5; будет устанавливать переменную x внутри внутренней функции, потому что var x; внутри if (false) { var x; } привязана к этой функции уже до того, как будет выполнен функциональный код.

Ответ 2

От dev.opera (выделено мной)

Конструкция try-catch-finally довольно уникальна. В отличие от других конструкций, он создает новую переменную в текущей области во время выполнения. Это происходит каждый раз, когда выполняется предложение catch, где выделенный объект исключения присваивается переменной. Эта переменная не существует внутри других частей script даже внутри той же области. Он создается в начале предложения catch, а затем уничтожается в конце его.

Таким образом, похоже, что единственное, что действительно входит в объем catch, - это само исключение. Другие действия, по-видимому, (или остаются) связаны с внешней областью улова (так что в примере глобальная область).

try {
    console.log(y); //ReferenceError so we enter catch
}
catch(e) {
    var x = 10;
    console.log(x); //10
}
console.log(e.message); //=> reference error

В ES5 эти строки могут быть релевантными (выделены жирным шрифтом/акцентом):

  • Пусть oldEnv - текущие контексты выполнения LexicalEnvironment.
  • Пусть catchEnv будет результатом вызова передачи NewDeclarativeEnvironment oldEnv как аргумент.

Также в конце этой части говорится:

ПРИМЕЧАНИЕ. Независимо от того, как управление оставляет блок, LexicalEnvironment всегда восстанавливается до прежнего состояния

Ответ 3

Поскольку он был стандартизован в ES3, предложение catch () {} (насколько мне известно) является единственной конструкцией javascript pre-ES2015, которая создает область блочного уровня, просто потому, что она является единственной конструкцией на уровне блока, которая поставляется с аргумент.

Именно по этой причине он использовался как polyfill для трансляций javascript следующего поколения, чтобы скомпилировать это:

ES2015:

{ let priv = 1; }
console.log(priv); // throws ReferenceError

:

ES5 и ниже:

try { throw void 0 } catch (priv) { priv = 1 }
console.log(priv); // throws ReferenceError

Ответ 4

Я был озадачен этим вопросом, поскольку, похоже, я уже сталкивался с подобной проблемой, не связанной с блоком catch в конкретном, но в определении функции. Теперь я только что вспомнил, и я на самом деле также писал об этой проблеме пару недель назад:).

Возьмите этот код:

function test(){
  var x = "Hi";

  (function(){
    console.log(x);
    var x = "Hi, there!";
  })();
}

Код: http://jsbin.com/alimuv/2/edit#javascript,live

Какой результат? Быстро глядя на него, можно предположить, что это "Привет", поскольку область действия переменной x взята из внешней области функции. Вместо этого распечатывается undefined. Причина та же, что и для проблемы блокировки catch: лексическая область, что означает, что область определения создается при определении функции, а не во время выполнения.