Статьи

Спецификация ECMAScript 5: LexicalEnvironment против VariableEnvironment

В этом посте рассматриваются некоторые детали того, как среды обрабатываются в спецификации ECMAScript 5 (ES5) [1]. В частности, в ES5 нет ни одной «текущей среды», а две: LexicalEnvironment и VariableEnvironment. Кусок кода в конце использует эти внутренние компоненты ES5 для получения различных результатов в Firefox и Chrome.

Примечание: вам будет намного легче понять этот пост, если вы уже знаете, как работают среды в ECMAScript 5.

Давайте начнем с краткого обзора сред и контекстов выполнения в ECMAScript 5:

Лексические среды содержат переменные и параметры. В настоящее время активная среда управляется с помощью стека
контекстов выполнения (который увеличивается и уменьшается синхронно со стеком вызовов). Вложенные области обрабатываются цепочечными средами: каждая среда указывает на свою
внешнюю среду (область действия которой охватывает его область действия). Чтобы включить лексическую область видимости, функции запоминают
область действия (= среду), в которой они были определены. Когда вызывается функция, создается новая среда для аргументов и локальных переменных. Внешняя среда этой среды является областью действия функции.

Другая важная для понимания концепция — это
подъем : в JavaScript любое объявление переменной var x = v; разделен на две части:

  • Объявление перемещено в начало окружающей функции как var x ;.
  • Инициализатор становится простым присваиванием x = v; это заменяет декларацию.

Это означает, что даже объявления, сделанные внутри блоков, таких как for, делаются в области видимости функции. Это доминирование области действия функции является основной мотивацией для сохранения двух текущих сред, как мы увидим ниже.

Структуры данных

(Лексическая) среда — это следующая структура данных [ES5, 10.2]:

  • Ссылка на внешнюю среду (нулевая в глобальной среде).
  • Запись среды отображает идентификаторы на значения. Существует два вида записей среды:

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

Контекст выполнения имеет следующие поля [ES5, 10.3]:

  • Среды: две ссылки на среды.

    • LexicalEnvironment (поиск и изменение существующих): разрешение идентификаторов.
    • VariableEnvironment (добавить новое): содержит привязки, сделанные объявлениями переменных и объявлениями функций.

    Оба обычно одинаковы. В следующих разделах объясняются ситуации, когда они расходятся.

  • ThisBinding: текущее значение this.

Обработка временных областей через LexicalEnvironment и VariableEnvironment

LexicalEnvironment и VariableEnvironment всегда одинаковы, за исключением одного случая: когда существует доминирующая внешняя область, и кто-то временно хочет войти во внутреннюю область. Во внутренней области видимости должно быть доступно несколько новых привязок, но все новые привязки, сделанные внутри нее, должны быть добавлены во внешнюю область видимости. Это делается следующим образом:

  • LexicalEnvironment временно указывает на новую среду, которая была помещена перед старой LexicalEnvironment. Новая среда содержит временные привязки внутренней области видимости.
  • VariableEnvironment не меняет своего значения и, таким образом, остается тем же, что и старая LexicalEnvironment, обозначая внешнюю область. Здесь добавляются новые привязки, которые также будут обнаружены при поиске через LexicalEnvironment, поскольку последний предшествует первому в цепочке среды.
  • После выхода из временной области старое значение LexicalEnvironment восстанавливается, и оно снова совпадает с VariableEnvironment.

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

  1. с оператором [ES5, 12.10]: объект, являющийся аргументом оператора, становится временной средой.
  2. предложение catch [ES5, 12.14]: исключение, являющееся аргументом этого предложения, доступно через временную среду.

Функции и их область применения: объявления против выражений

Другой областью, где можно наблюдать разницу между LexicalEnvironment и VariableEnvironment, являются определения функций: объявления функций используют VariableEnvironment в качестве области действия, а выражения функций используют LexicalEnvironment. Для объявлений функций мотивация состоит в том, чтобы переместить объявление в (доминирующую) область действия функции. Таким образом, они должны видеть только то, что существует на этом уровне. В следующем примере кода показано, как это работает.

    var foo = "abc";
    with({ foo: "bar" }) {
       function f() {
           console.log(foo);
       }
       f();
    }

Консоль выводит «bar» в Firefox и Rhino, «abc» в V8 (Chrome, node.js). Последнее более точно отражает спецификацию, первое, вероятно, то, что ожидали бы большинство программистов.

Если вместо этого вы используете выражение функции, на всех платформах выводится «bar».

    var foo = "abc";
    with({ foo: "bar" }) {
        (function() { console.log(foo); }());
    }

[Спасибо Аллену Уирфс-Броку за помощь в понимании некоторых тонкостей LexicalEnvironment и VariableEnvironment.]

Рекомендации:

  1. Стандарт ECMA-262: спецификация языка ECMAScript

 

С http://www.2ality.com/2011/04/ecmascript-5-spec-lexicalenvironment.html