Объявления переменных и функций всплывают на верх области видимости, у var и function (та что declaration) - это внешняя функция или модуль (если на верхнем уровне дело происходит), у let и const - блок, то есть после всплытия, внутри движка Ваш код будет выглядеть так:
var foo;
function bar() {
var foo; // эта foo перекрывает foo из внешней области видимости, и поэтому в функции используется она
if (!foo) {
foo = 10;
}
console.log(foo);
}
foo = 1;
bar(); //--> 10
Так же важно знать, что хотя let и const всплывают наверх блока, но использовать их до фактического места объявления нельзя, так будет ошибка:
{
console.log(x); // ReferenceError: Cannot access 'x' before initialization
let x;
}
Это механизм защиты "от дурака" и называется он "мертвое всплытие". По этому всегда лучше использовать let или const, притом const предпочтительнее если переменная не меняется, так как дает больше гарантий.