Позволяет ли 'let' переопределить глобальное объявление и выдает ReferenceError?

Я просматривал пример документации «Разница между var и let» и проверял, что при вызове необъявленной переменной глобальная область автоматически предоставляет для нее объявление (поэтому следующий фрагмент не выдает ошибку ни в одной из переменных):

x = 3;
console.log(x);

(function() {
  y=x+39;
})()
console.log(y);

Однако, когда одна переменная объявлена ​​с let после присваивания в той же глобальной области:

x=3;
let x = 42;
console.log(x);

Выдается одна из следующих ошибок:

ReferenceError: x не определено (Chromium)

ReferenceError: невозможно получить доступ к лексическому объявлению x перед инициализацией (Firefox)

Я понимаю, что let не позволяет поднимать x, но поскольку на него ранее ссылались (подразумевая автоматическое объявление из глобальной области видимости), не должно ли в этом случае происходить повторное объявление?

SyntaxError: Identifierx уже объявлен

И поэтому ошибка выше выброшена?

Я также понимаю, что в строгом режиме первый сниппет вызовет ReferenceError, значит ли это, что let навязывает это конкретное правило строгого режима (все переменные должны быть объявлены) в глобальном масштабе?


person CPHPython    schedule 03.01.2017    source источник
comment
let также поднимается, вы просто обращаетесь к нему во временной мертвой зоне. Я не понимаю, при чем здесь строгий режим.   -  person Bergi    schedule 03.01.2017
comment
@Bergi Я не был полностью осведомлен о TDZ, Я предполагал, что необъявленная переменная будет автоматически объявлена ​​в глобальной области (в настоящее время документы показывают только двойное объявление let). Поскольку доступ к переменной до ее собственного объявления let вызывает ReferenceError, я думал, что это связано с поведением/правилом строгого режима, но теперь я понимаю, что это связано с тем, что let поднимает переменную.   -  person CPHPython    schedule 04.01.2017


Ответы (3)


Вы правы, это странное поведение. Причина, по которой он выдает эти ошибки, заключается в том, что он думает, что вы пытаетесь присвоить значение 3 вашей переменной let вместо глобального значения. Как уже упоминалось, это приводит к временной мертвой зоне при подъеме.

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

- Источник (ECMAScript, 8-е издание )

Этот код показывает, где размещение кода вызывает TDZ:

// Accessing `x` here before control flow evaluates the `let x` statement
// would throw a ReferenceError due to TDZ.
// console.log(x);

let x = 42;
// From here on, accessing `x` is perfectly fine!
console.log(x);

Вы можете видеть, что обертывание let внутри собственного блочного блока исправляет это:

x=3;
{
let x = 42;
console.log(x); // 42
}

Кроме того, вы можете явно определить глобальную переменную для объекта window:

window.x=3;

let x = 42;
console.log(x);  // 42
person Alex W    schedule 03.01.2017
comment
как насчет: `var a = 1; функция fn() { console.log(a); пусть а = 2; } фн(); ` - person ; 31.05.2018
comment
@AntonYaskevich То же самое, вы пытаетесь зарегистрировать x до того, как оператор будет оценен. - person Alex W; 31.05.2018

Вы ознакомились с let документами по адресу MDN? Они описывают временную мертвую зону и ошибки с let.

ES6 поднимает переменную let на вершину своей области видимости. В отличие от переменной var, при использовании let вы не должны обращаться к переменной до ее объявления. Это не удается сделать с ReferenceError (он же временная мертвая зона).

person Konstantin A. Magg    schedule 03.01.2017
comment
но не инициализирован ли x значением 3, которое по умолчанию является глобальным объявлением в первом выражении. Таким образом, он должен иметь ссылку на оператор № 2 независимо от того, поднимает ли let переменную или нет. Каковы ваши взгляды? - person Lokesh Devnani; 03.01.2017
comment
let x; поднимается наверх области видимости — в данном случае глобальной области видимости. Оператор x=3; не может создать новый var, так как в этой области имя уже зарезервировано для нашего let. Он также не может получить доступ к переменной x, так как она еще не объявлена. - person Konstantin A. Magg; 03.01.2017
comment
Таким образом, это поведение, которое предотвращает использование переменных перед объявлением. Это имеет смысл. Тем не менее, я должен буду изучить его больше, чтобы быть уверенным. Спасибо - person Lokesh Devnani; 03.01.2017
comment
@KonstantinA.Magg ваши слова в вашем комментарий позволил мне понять это лучше, чем слова в вашем ответе. Спасибо. - person CPHPython; 04.01.2017

Как объяснил Константин А. Мэгг, это связано с тем, что let переменные поднимаются и пытаются сослаться на них до броска инициализации (временная мертвая зона).

Если вы этого не хотите, вы можете разделить код на разные скрипты:

<script>
x = 3;
console.log(x); // 3
</script>

<script>
let x = 42;
console.log(x); // 42
</script>

Примечание x = 3 перейдет в строгий режим.

person Oriol    schedule 03.01.2017