Хойстинг | Hoisting

Хойстинг (Hoisting) - дословно всплытие переменных.

Интерпретатор JavaScript всегда незаметно для нас перемещает («поднимает») объявления функций и переменных в начало области видимости. Формальные параметры функций и встроенные переменные языка, очевидно, изначально уже находятся в начале. Это значит, что этот код:

function foo() { 
    bar(); 
    var x = 1; 
}

на самом деле интерпретируется так:

function foo() { 
    var x; 
    bar(); 
    x = 1; 
}

Это связано с тем, что в JavaScript используется область видимости на уровне функций.  Оказывается, не важно, будет ли вообще выполнена строка, в которой происходит объявление. Следующие две функции эквивалентны:

function foo() { 
    if (false) { 
        var x = 1; 
    } 
    return; 
    var y = 1; 
} 
// эквивалентно
function foo() { 
    var x, y; 
    if (false) { 
        x = 1; 
    } 
    return; 
    y = 1; 
}

Важно - обратить внимание, что присваивание значений переменным не поднимается вместе с их объявлением. Поднимаются только объявления переменных. В случае с функциями, поднимается вся функция целиком.

function test() { 
    foo(); // TypeError "foo is not a function" 
    bar(); // "this will run!" 
    var foo = function () { //  функциональное выражение Named Function Expression, присвоенное локальной переменной 'foo' 
        alert("this won't run!"); 
    } 
    function bar() { // объявление функции с именем 'bar' 
        alert("this will run!"); 
    } 
} 
test();

в этом случае поднимается только функция bar. Идентификатор foo также поднимается, но не анонимная функция — она остаётся на месте.

Именованные функциональные выражения | Named Function Expression (NFE)

Можете давать имена функциям, определённым с помощью функциональных выражений, используя синтаксис определения функций. Это не приводит к объявлению функции, а следовательно, имя функции ни добавляется в область видимости, ни поднимается вместе с телом функции в начало области видимости. Пример:

foo(); // TypeError "foo is not a function" 
bar(); // работает 
baz(); // TypeError "baz is not a function" 
spam(); // ReferenceError "spam is not defined" 
 
var foo = function () {}; // анонимное функциональное выражени (поднимается 'foo') 
function bar() {}; // объявление функции (поднимаются 'bar' и тело функции) 
var baz = function spam() {}; // именованное функциональное выражение (поднимается только 'baz') 
 
foo(); // работает 
bar(); // работает 
baz(); // работает 
spam(); // ReferenceError "spam is not defined" 

Правильное написание кода:

Чтобы избежать проблем и непонимания - объявлять переменные в начале области видимости. Например:

var foo = 1; 
function bar() { 
    if (!foo) { 
        var foo = 10; 
    } 
    alert(foo); 
} 
bar();
// лучше заменить на
var foo = 1; 
function bar() { 
    var foo;
	if (!foo) { 
        foo = 10; 
    } 
    alert(foo); 
} 
bar();