Возьмем следующий код:

a = 2;
var a;
console.log( a );

Как вы думаете, что выведет команда console.log(..)? // 2

Глядя на команду var a = 2;, вы, вероятно, думаете о ней как об одной команде. Но#JavaScript в действительности воспринимает ее как две команды: var a; и a = 2;. Первая команда (объявление) обрабатывается в фазе компиляции. Вторая команда (присваивание) остается на своем месте до фазы исполнения.

Объявления переменных и функций «перемещаются» из их текущей позиции в начало кода. При этом происходит#поднятие (#hoisting ).

Рассмотрим другой фрагмент кода:

console.log( a );
var a = 2;

Программа выведет undefined

foo();
function foo() {
	console.log( a ); // undefined
	var a = 2;
}

Объявление функции foo (которое в данном случае включает «значение», то есть код функции) поднимается, чтобы вызов в первой строке мог успешно выполниться.

function foo() {
	var a;
	console.log( a ); // undefined
	a = 2;
}
 
foo();

Объявления функций поднимаются, как вы только что видели. Функциональные выражения — нет.

foo(); // не ReferenceError, но TypeError!
	var foo = function bar() {
// ...
};

Идентификатор переменной foo поднимается и присоединяется к внешней области видимости (глобальной) программы, так что для foo() ошибка#ReferenceError не происходит. Но у foo значения еще нет (как было бы в том случае, если бы это было полноценное объявление функции вместо выражения). Соответственно foo() пытается вызвать значение undefined, что приводит к недействительной операции#TypeError.

foo(); // TypeError
bar(); // ReferenceError