Смешивание функций и условных выражений const / let внутри вызывает ошибку ссылки в Safari

#javascript #safari #scope

#javascript #safari #область видимости

Вопрос:

У нас есть что-то вроде этого

 if(true) {
    const a = 1;
    function myFunc() {
        alert(a);
    }

    myFunc();
}
  

В Safari 11 это вызывает "ReferenceError: Can't find variable: a" .

Тот же код работает без ошибок в Chrome и Firefox.

Использование "strict mode" в Safari решает проблему.

Я думаю, что основная проблема заключается в разной области действия const a и function myFunc . Последняя, по сути, является глобальной функцией из-за того, что условный оператор не создает область действия блока для функций внутри него (я полагаю, по устаревшим причинам), как это происходит для let и const .

Мне интересно, имеет ли Safari право в этом случае, потому что мы смешиваем вещи с разной областью видимости.

Есть ли какой-нибудь официальный ресурс, который объясняет этот случай? Я не нахожу упоминаний об этом поведении ни на сайтах caniuse, ни на mdn

Комментарии:

1. Взгляните на ecma-international.org/ecma-262/10.0 /…

Ответ №1:

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

Поскольку этот синтаксис не был определен в спецификации и был разрешен движками javascript, разные движки выполняли разные действия. Некоторые сделали это синтаксической ошибкой, другие обработали объявления функций в областях блоков, поскольку они были выражениями функций. Некоторые движки обрабатывали объявления функций в области блока как несколько поднятых объявлений в одной области.

Начиная с ES2015, объявления функций являются частью спецификации, и существует два способа их обработки:

  • Стандартная веб-семантика
  • Устаревшая веб-семантика

Стандартная семантика

При стандартной семантике объявления функций преобразуются в выражения функций, объявляются с let ключевым словом и помещаются в начало блока. Стандартная семантика действует в строгом режиме.

Таким образом, в строгом режиме ваш код будет обрабатываться движком javascript так, как если бы он был написан следующим образом:

 if(true) {
    let myFunc = function() {
       alert(a);
    }

    const a = 1;
    myFunc();
}
  

Устаревшая веб-семантика

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

  1. Функция объявляется и ссылается в одном блоке
  2. Функция объявлена и, возможно, используется в пределах одного блока, но также ссылается на определение внутренней функции, которое не содержится в этом же блоке.
  3. Функция объявлена и, возможно, используется в одном блоке, но также ссылается в последующих блоках.

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

Ваш код в нестрогом режиме обрабатывается движком javascript как:

 var varMyFunc;

if(true) {
    let myFunc = function() {
       alert(a);
    }

    const a = 1;

    varMyFunc = myFunc;    // at the place of function declaration
   
    myFunc();
}
  

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