Почему в JavaScript переменная, объявленная через var, всплывает и инициализируется значением undefined?
Что проверяют
Знаете ли вы, что происходит на этапе создания окружения, до выполнения кода. По ответу видно, отличаете ли вы var от let по существу или на уровне «let новее, используйте его».
Как отвечать
Движок обрабатывает код в два прохода. Сначала для области видимости создаётся окружение, и в него заносятся все объявления: привязка для var появляется сразу инициализированной значением undefined, объявление функции попадает целиком, вместе с телом. Только потом выполняются инструкции, и присваивание срабатывает ровно там, где написано.
console.log(a); // undefined
var a = 5;
console.log(b); // ReferenceError
let b = 5;Дальше важное уточнение, которое и отличает сильный ответ: let и const тоже всплывают. Их привязка создаётся в начале блока, но остаётся неинициализированной, и обращение к ней до строки объявления бросает ReferenceError. Этот промежуток называют временной мёртвой зоной. Разница не в том, всплывает или нет, а в том, чем привязка заполнена до объявления.
Ещё несколько следствий, которые уместно назвать самому:
varживёт в функции, блок его не ограничивает, поэтому объявление внутриifвидно и после блока;- повторное
var xне ошибка и не сбрасывает значение, а повторноеlet xв той же области этоSyntaxError; varна верхнем уровне скрипта создаёт свойство глобального объекта,letиconstнет.
Где спотыкаются
Говорят, что let не всплывает вовсе. Если бы не всплывал, обращение до объявления ушло бы во внешнюю область видимости и взяло значение оттуда. Оно бросает ошибку, значит привязка существует, просто пустая.
Второе: пользуются typeof как безопасной проверкой существования. Для var вернётся строка "undefined", а для let в мёртвой зоне typeof точно так же бросит ReferenceError.
Третье: путают всплытие объявления функции и функционального выражения. function f() {} можно вызвать выше по файлу, а var f = function () {} до строки присваивания это undefined, и вызов даст TypeError.