Хойстинг (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();