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

Какова четкая семантика блочных функций в ES6?

Я пытаюсь обернуть голову новыми стандартизованными функциями уровня блока в ES6, читая исходную спецификацию. Мое поверхностное понимание было:

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

Однако это еще больше осложняется тем фактом, что часть этих семантик указана как "необязательная" и обязательна только для веб-браузеров (Приложение B). Поэтому я хотел бы заполнить следующую таблицу:

                                             |  Visible outside of block?  |  Hoisted? Up to which point?  |   "TDZ"? |
------------------------------------------------------------------------------------------------------------------------
|   Non-strict mode,   no "web extensions"   |                             |                               |          |
|   Strict mode,       no "web extensions"   |                             |                               |          |
|   Non strict mode,   with "web extensions  |                             |                               |          |
|   Strict mode,       with "web extensions" |                             |                               |          |

Также мне непонятно, что означает "строгий режим" в этом контексте. Это различие, по-видимому, представлено в приложении B3.3 в качестве части дополнительных шагов для выполнения декларации функции:

1. If strict is false, then
...

Однако, насколько я вижу, strict относится к внутреннему слоту [[Strict]] объекта функции. Означает ли это, что:

// Non-strict surrounding code

{
    function foo() {"use strict";}
}

следует рассматривать как "строгий режим" в таблице выше? Однако это противоречит моей первоначальной интуиции.

Пожалуйста, имейте в виду, что меня интересует сама спецификация ES6, независимо от фактических несоответствий в реализации.

4b9b3361

Ответ 1

Насколько я вижу, strict относится к внутреннему слоту [[Strict]] объекта функции.

И да. Это относится к строгости функции (или скрипта), в которой происходит блок, содержащий объявление функции. Не в строгости функции, которая должна (или не должна) быть объявлена.

"Веб-расширения" применимы только к небрежному (нестрогому) коду и только в том случае, если внешний вид оператора функции "нормальный", то есть, например, если его имя не вступает в противоречие с формальным параметром или лексически объявленная переменная.

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

Таким образом, мы в основном имеем

                 |      web-compat               pure
-----------------+---------------------------------------------
strict mode ES6  |  block hoisting            block hoisting
sloppy mode ES6  |  it complicated ¹        block hoisting
strict mode ES5  |  undefined behavior ²      SyntaxError
sloppy mode ES5  |  undefined behavior ³      SyntaxError

1: смотри ниже.Предупреждения запрашиваются.
2: Как правило, SyntaxError выбрасывается
3: примечание в ES5.1 §12 говорит о "значительных и непримиримых вариациях среди реализаций" (таких как эти).Предупреждения рекомендуются.

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

Это лучше объяснить с помощью кода:

function enclosing(…) {
    …
    {
         …
         function compat(…) { … }
         …
    }
    …
}

работает так же, как

function enclosing(…) {
    var compat₀ = undefined; // function-scoped
    …
    {
         let compat₁ = function compat(…) { … }; // block-scoped
         …
         compat₀ = compat₁;
         …
    }
    …
}

Да, это немного сбивает с толку, имея две разные привязки (обозначенные индексами 0 и 1) с одним и тем же именем. Теперь я могу кратко ответить на ваши вопросы:

Видимо за пределами блока?

Да, как var. Однако существует вторая привязка, которая видна только внутри блока.

Водрузили?

Да - дважды.

До какой точки?

Как для функции (однако инициализируется с помощью undefined), так и для блока (инициализируется с помощью объекта функции).

"Протяжен"?

Не в смысле временной мертвой зоны объявленной лексически переменной (let/const/class), которая генерирует ссылки, нет. Но до того, как объявление функции встретится при выполнении тела, переменная области действия не undefined (особенно перед блоком), и вы также получите исключение, если попытаетесь вызвать ее.


Просто для справки: в ES6 описанное выше поведение было задано только для блоков в областях действия функций. С ES7 то же самое относится к блокам в eval и global областях.

Ответ 2

Я не знаю, откуда твоя путаница. Согласно 10.2.1, это очень ясно, что есть или нет "в строгом режиме". В вашем примере внутренний слот foo [[Strict]] будет true действительно и будет находиться в строгом режиме, но его размещение в блоке не будет. Первое предложение (указанное вами) относится к блоку хостинга, а не к содержащемуся в нем содержимому. Блок в вашем фрагменте не находится в строгом режиме и, следовательно, этот раздел применяется к нему.