ECMAScript: где можно найти спецификацию о доступности переменных let/const
В спецификации ECMAScript, где мы можем найти четкую спецификацию, почему let
а также const
не доступны вне Лексических Сред, созданных с помощью BlockStatements (в отличие от переменных, объявленных с var
)?
Если BlockStatements теперь создают новые лексические среды, то let
а также const
объявления не должны создавать переменные, доступные вне этой лексической среды, но var
переменные должны. Я пытаюсь понять, где именно это поведение указано в последней спецификации ECMAScript.
Из деклараций 13.3.1 Let и Const:
Объявления let и const определяют переменные, относящиеся к области Lexical Environment запущенного контекста выполнения. Переменные создаются, когда создается экземпляр их содержащей Lexical Environment, но к ним нельзя получить доступ, пока не будет оценена переменная LexicalBinding.
Оператор var объявляет переменные, относящиеся к области VariableEnvironment текущего контекста выполнения. Переменные var создаются, когда создается экземпляр их содержащей Lexical Environment и при создании инициализируются как неопределенные.
Как видно, оба объявления переменных создают переменные, когда создается экземпляр их содержащей Lexical Environment. Что в случае BlockStatement - это когда компилятор входит в блок.
Компоненты Lexical Environment и VariableEnvironment контекста выполнения всегда являются лексическими средами.
1 ответ
Как вы видели в описании var
, он ограничен контекстом выполнения VariableEnvironment
, Есть верхний уровень VariableEnvironment
и затем создается новая, когда вы вводите функцию, а затем в этой части о контекстах выполнения она говорит следующее:
Компоненты LexicalEnvironment и VariableEnvironment контекста выполнения всегда являются лексическими средами. Когда создается контекст выполнения, его компоненты LexicalEnvironment и VariableEnvironment изначально имеют одинаковое значение.
Итак, в начале функции LexicalEnvironment и VariableEnvironment являются одним и тем же.
Затем в разделе 13.2.13 Семантика времени выполнения: блок оценки: { } вы можете увидеть, что новый LexicalEnvironment
создается при входе в блок, а предыдущий восстанавливается при выходе из блока. Но нет никакого упоминания о новом VariableEnvironment
когда вы входите или выходите из блока (потому что он остается постоянным внутри функции).
Итак, с let
а также const
находятся в области LexicalEnvironment, в которой они объявлены и являются локальными для блока, он не будет доступен за пределами блока.
Но, var
относится к VariableEnvironment
который создается только для всей функции, а не для блока.
let
а также const
переменные не могут быть доступны вне их LexicalEnvironment, потому что их определения не находятся в иерархии областей действия, как только контекст выполнения покидает их блок (как только вы покидаете блок, их LexicalEnvironment по существу выталкивается из стека и больше не находится в область поиска цепочки для интерпретатора, чтобы найти переменные).
Когда спецификация добавляет это:
[
let
а такжеconst
] переменные создаются, когда создается экземпляр их содержащей Lexical Environment, но к ним нельзя получить доступ каким-либо образом, пока не будет оценена переменная LexicalBinding.
Это означает, что вам отказано в доступе к ним даже в их собственной лексической среде, пока их определение не будет оценено. С точки зрения непрофессионала, это означает, что они не подняты к вершине их объема как var
и, следовательно, не может быть использовано до тех пор, пока не будет определено их. Это реализуется путем не инициализации let
или же const
переменная в LexicalEnvironment
пока его заявление не работает и GetBindingValue()
Операция, которая ищет переменную, увидит, что она еще не инициализирована, и выдаст ReferenceError
, var
переменные инициализируются немедленно undefined
поэтому они не вызывают это ReferenceError
,
Вы можете увидеть, как это работает в этом коде:
let x = 3;
function test() {
x = 1;
let x = 2;
console.log("hello");
}
test();
На let x = 3
строка переменная x
инициализируется во внешнем LexicalEnvironment.
Затем, когда вы звоните test()
в начале этой функции создается новое LexicalEnvironment, новое объявление для x
в этом блоке помещается в этот новый LexicalEnvironment
, но еще не инициализирован.
Затем вы получите к x = 1
заявление. Переводчик смотрит вверх x
, находит его в текущем LexicalEnvironment
, но он неинициализирован, поэтому он бросает ReferenceError
,
На вопрос в вашем комментарии:
Я переворачивал спецификацию с ног на голову, но с трудом обнаружил, что VariableEnvironments создаются только для функций. Не могли бы вы добавить ответ, показывающий, какие шаги в спецификации вы выполняете, чтобы прийти к такому выводу?
Вы должны просто пройти через все места в спецификации, где VariableEnvironment
создан, и вы обнаружите, что это происходит только в начале выполнения функции и на верхнем уровне.
Например, вот один из тех мест: PrepareForOrdinaryCall. Есть несколько других.
Но нигде не описано, что это происходит для начала блока, только для начала функции.
То, как эти спецификации написаны, они описывают, когда что-то происходит, а не когда это не происходит (что имеет некоторый логический смысл), но это значит, что доказать, что что-то не происходит, вы не можете найти то, что говорит это действительно случается.