На самом деле это вообще невозможно.
TL; DR: В вашем коде переменная bar
равна объявлен и определен, но не инициализирован.
Первая ошибка верна: bar
был объявлен дважды.
Также обратите внимание, что это время компиляции SyntaxError
, так что это происходит до того, как код будет оценен, поэтому на него не влияет сгенерированное исключение внутри объявления переменной:
//SyntaxError
console.log('Evaluating code') //Never runs
let foo = 'bar'
let foo = 'baz'
Но вторая ошибка не столь очевидна: почему bar
просто undefined
?
После большого количества поиск в спецификации ECMAScript 6 , я нашел источник проблемы. Это «ошибка» (или, по крайней мере, ситуация, о которой не позаботились) в самом spe c, но, к счастью, это очень редко за пределами консоли JS.
Возможно, вы знаете что переменные let
и const
имеют так называемую временную мертвую зону , которая выбрасывает ReferenceError
s при попытке поиска или назначения переменных до их объявления:
/* Just to make console fill the available space */
.as-console-wrapper{max-height:100% !important;}
<!-- Using separate scripts to show all errors -->
<script>
console.log(foo) //ReferenceError
const foo = 'bar'
</script>
<script>
bar = 'baz' //ReferenceError
let bar
</script>
Это связано с тем, что привязки этих переменных созданы до выполнения содержащего блока кода, но не инициализированы пока не будет проверен оператор объявления переменной. Попытка извлечь или изменить значение неинициализированной привязки всегда приводит к ReferenceError
.
Теперь давайте проверим оценку (и const
) оператора *1055* LexicalBinding оператора (variable = value
пара), определяется следующим образом:
LexicalBinding : BindingIdentifier Initializer
- Пусть bindingId будет StringValue BindingIdentifier .
- Пусть lhs будет ResolveBinding ( bindingId * 1081) *).
- Пусть rhs будет результатом оценки Initializer .
- Let value be GetValue ( rhs ) .
- ReturnIfAbrupt ( значение ) .
- If IsAnonymousFunctionDefinition ( Инициализатор ) имеет значение true, тогда
- Пусть hasNameProperty будет HasOwnProperty ( значение , "имя").
- ReturnIfAbrupt ( hasNameProperty ).
- Если hasNameProperty равно false, выполнить SetFunctionName ( значение , bindingId ).
- Return InitializeReferencedBinding ( lhs , value ) .
Не вдаваясь в детали, я бы хотел выделить самые важные вещи:
BindingIdentifier
- это имя переменной Initializer
- это значение, которое ему присваивается ReturnIfAbrupt()
- абстрактный алгоритм, который возвращается из вызывающий с его аргументом, если его аргумент является записью завершения, которая представляет внезапное завершение (например, выброшенное исключение) InitializeReferencedBinding()
инициализирует данное связывание
Проблема возникает, когда исключение, выданное во время оценки Initializer
.
Когда это произойдет, это:
GetValue(rhs)
... вернет внезапное завершение, поэтому следующая строка:
ReturnIfAbrupt(value)
... возвращается из оператора let
(или const
) с неожиданной записью о завершении (т. Е. Повторные броски исключение), поэтому строка:
InitializeReferencedBinding(lhs, value)
... не будет работать вообще, поэтому привязка переменной остается неинициализированной и продолжает выдавать RefereceError
s , когда вы попытаться найти его или присвоить ему.
Сообщение об этих ошибках (foo is not defined
) еще более запутанно и неуместно, но это зависит от реализации, поэтому я не могу рассуждать об этом; однако, вероятно, это из-за другого необработанного случая.