Лексическая область видимости в ES6 / Node

Я пытаюсь понять лексическую область видимости ES6 (используя среду выполнения узла). Учтите следующее:

'use strict';
let x = 10;
function f() {
   console.log(x);
   console.log(y); // This should crash
}

let y = 5;
f();

Перефразируя книгу О'Рейли "Learning Javascript":

Лексическая область видимости означает, что любые переменные, находящиеся в области видимости, из которой вы определяете функцию (в отличие от того, когда вы ее вызываете), находятся в области видимости функции.

Однако, когда я запускаю эту программу (через узел), она выводит: 10 5

Разве здесь вызов console.log (y) не нарушает правила лексической области видимости? Если нет, то почему?

Изменить: для справки в будущем, похоже, что автор учебника (Learning Javascript 3rd Edition O'Reilly) недавно перечислил этот пример как ошибку в «Подтвержденных исправлениях». на http://www.oreilly.com/catalog/errata.csp?isbn=0636920035534


person biff pumpkin    schedule 15.04.2016    source источник
comment
Объявления переменных поднимаются в начало блока.   -  person Barmar    schedule 16.04.2016
comment
@Barmar, если вы получите доступ к let до его определения, вы получите ошибку временной мертвой зоны. Если вы поменяете местами let y = 5; и f(), вы получите сообщение об ошибке, а не undefined в журнал.   -  person Benjamin Gruenbaum    schedule 16.04.2016
comment
Он не получит к ней доступа, пока не вызовет f(), то есть после инициализации переменной.   -  person Barmar    schedule 16.04.2016
comment
@BenjaminGruenbaum Это временная мертвая зона, а не лексическая мертвая зона.   -  person Barmar    schedule 16.04.2016
comment
Верно, но это вообще не имеет отношения к подъему.   -  person Benjamin Gruenbaum    schedule 16.04.2016
comment
Поднятие - это причина, по которой переменная находится в области действия внутренней функции, даже если объявление происходит после определения функции.   -  person Barmar    schedule 16.04.2016
comment
Это также не имеет ничего общего с подъемом - это просто проверяется достоверность переменных факта при обращении - что случается, когда вызывается f, который после определения переменной. Если бы f был, например, IIFE, код выбросил бы. Подъем - просто неправильный термин для этого.   -  person Benjamin Gruenbaum    schedule 16.04.2016
comment
В ES 2015 даже явно упоминается var поднятые операторы: это относится к операторам var и спискам формальных параметров некоторых нестрогих функций.   -  person Benjamin Gruenbaum    schedule 16.04.2016
comment
@Benjamin: Итак, подъем определяется как инициализированная привязка?   -  person Felix Kling    schedule 18.04.2016
comment
@FelixKling, это хороший вопрос, подъем определяется только в спецификации в примечаниях и в конструкции HoistableDeclaration. В примечаниях он всегда рассматривает подъем как инициализированную привязку (примечания в 12.1.5, 13.7.5.9, 14.1.3, 14.4.2, 18.2.1.2 - также упоминаются конфликты подъема между var и let) - как для фактической спецификации (не примечания в спецификации) у нас есть HoistableDeclaration, который является объявлением функции или объявлением генератора. Не похоже, что это действительно так однозначно, поскольку спецификация не фактически определяет подъем.   -  person Benjamin Gruenbaum    schedule 18.04.2016
comment
На самом деле я только видел, что раньше это означало инициализированное связывание в esdiscuss и т. Д., Но я думаю, что это новая разница, поскольку в ES5 они были неотличимы.   -  person Benjamin Gruenbaum    schedule 18.04.2016


Ответы (1)


Как упоминал Бенджамин Грюнбаум, let и const вообще не поднимаются.

Фактически, есть новые правила, которые применяются к let и const, такие как…

временная мертвая зона

Вот если бы это были var декларации, все было бы ясно. Но с let и const ES6 представляет новую концепцию временной мертвой зоны . Это включает новую тонкую динамику.

Давайте посмотрим на два примера:

Классический подъем будет работать в следующем примере:

'use strict';
var x = 10;

console.log(x);
console.log(y); // This should NOT crash

var y = 5;

Но если бы мы заменили объявления var на объявления let, это привело бы к сбою:

'use strict';
let x = 10;

console.log(x);
console.log(y); // This crashes: ReferenceError: can't access lexical declaration `y' before initialization

let y = 5;

Почему это вылетает?

Потому что, в отличие от присваиваний var, доступ к переменным, определенным с помощью let перед фактическим оператором let, недействителен (они находятся во временной мертвой зоне).

2. В этом случае временная мертвая зона

Однако в этом случае временная мертвая зона не является проблемой. Почему?

Потому что, хотя мы заранее определяем функцию с помощью оператора console.log(y), фактический вызов функции и, следовательно, доступ к переменной происходит только в конце кода. Таким образом, привязки переменных оцениваются только на этом этапе (еще раз спасибо, @BG):

'use strict';
let x = 10;
function f() {
   console.log(x);
   console.log(y); // This should not yet crash
}

let y = 5;
f(); // console.log(y) is only called here

Если вы измените порядок let y = 5; и f();, ваш код выйдет из строя.

person nils    schedule 15.04.2016
comment
Это не подъем - это просто привязки фактов оцениваются при вызове функции, а не до нее - функция вызывается после определения y. - person Benjamin Gruenbaum; 16.04.2016
comment
Значит, движок вообще не создает ссылки на y перед оператором let y = 5;? И, таким образом, когда возникает ошибка TDZ, движок проверяет, есть ли объявление let позже? - person nils; 16.04.2016
comment
Первая часть: Да. Вторая часть - наоборот, выдается ошибка TDZ, если движок не может найти привязку. В рассматриваемом случае привязка уже определена, поэтому ошибка TDZ не возникает. - person Benjamin Gruenbaum; 16.04.2016
comment
Правильно, мне просто интересно, как движок знает разницу между полным отсутствием привязки и отсутствующей привязкой из-за TDZ (это технические детали, мне просто любопытно, потому что это не работает, как я ожидал :)). - person nils; 16.04.2016
comment
На самом деле JavaScript не интерпретируется построчно - все тело функции должно быть прочитано сначала, а привязки и порядок извлечены. Это сделано, потому что это необходимо для var привязок, class объявлений и function объявлений, которые подняты. - person Benjamin Gruenbaum; 16.04.2016
comment
Двигатели не обрабатывают вещи построчно. Привязка создается для каждой переменной в заданной области в начале, до начала оценки, но для let и const, но привязка остается неинициализированной, тогда как var инициализирует привязку для undefined. Привязки let / const инициализируются, когда строки let/const фактически оцениваются, и попытка доступа к неинициализированной привязке вызовет исключение. - person loganfsmyth; 16.04.2016
comment
Спасибо вам обоим за ваши ответы. Еще так много предстоит понять ... - person nils; 16.04.2016
comment
Думаю, это зависит от того, что вы понимаете под подъемником. Согласно спецификации, привязки для let и const создаются (поднимаются?), Но не инициализируются. Вот как среда выполнения узнает, что привязка находится в TDZ. - person Felix Kling; 18.04.2016