Возьмем следующий код:
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