[Medium] 📄 Hoisting
1. Что такое Hoisting?
Выполнение JavaScript можно рассматривать как две фазы: создание и выполнение.
var name = 'Pitt';
console.log(name); // выводит Pitt
При hoisting движок концептуально сначала обрабатывает объявление, а затем присваивание:
// создание
var name;
// выполнение
name = 'Pitt';
console.log(name);
Функции ведут себя иначе, чем переменные. Объявление функции привязывается во время фазы создания:
getName();
function getName() {
console.log('string'); // выводит string
}
Это работает, потому что объявление функции поднимается до вызова во время выполнения:
// создание
function getName() {
console.log('string');
}
// выполнение
getName();
Примечание: при использовании функциональных выражений порядок объявления и присваивания по-прежнему имеет значение.
В фазе создания объявления функций имеют более высокий приоритет, чем объявления переменных.
Правильно (Correct)
name = 'Yumy';
console.log(name); // выводит Yumy
var name;
// --- Эквивалентно ---
// создание
var name;
// выполнение
name = 'Yumy';
console.log(name); // выводит Yumy
Неправильно (Wrong)
console.log(name); // выводит undefined
var name = 'Jane';
// --- Эквивалентно ---
// создание
var name;
// выполнение
console.log(name); // выводит undefined, потому что присваивание ещё не произошло
name = 'Pitt';
2. Что выведет name?
whoseName();
function whoseName() {
if (name) {
name = 'Nini';
}
}
var name = 'Pitt';
console.log(name);
Ответ
// создание
function whoseName() {
if (name) {
name = 'Nini';
}
}
var name;
// выполнение
whoseName();
name = 'Pitt';
console.log(name); // выводит Pitt
Внутри whoseName() значени е name начинается как undefined, поэтому ветка if (name) не выполняется.
После этого name получает значение 'Pitt', поэтому финальный вывод всё равно Pitt.
3. Объявление функции vs объявление переменной: приоритет Hoisting
Вопрос: функция и переменная с одинаковым именем
Предскажите вывод этого кода:
console.log(foo);
var foo = '1';
function foo() {}
Распространённые неправильные ответы
Многие думают, что выведется:
undefined(предполагая, чтоvarподнимается первым)'1'(предполагая, что присваивание уже произошло)- Ошибка (предполагая конфликт одинаковых имён)
Фактический вывод
[Function: foo]
Почему?
Этот вопрос пров еряет правило приоритета hoisting:
Приоритет hoisting: объявление функции > объявление переменной
// Исходный код
console.log(foo);
var foo = '1';
function foo() {}
// Эквивалент после hoisting
// Фаза 1: создание (hoisting)
function foo() {} // 1. объявление функции поднимается первым
var foo; // 2. объявление переменной поднимается (не перезаписывает функцию)
// Фаза 2: выполнение
console.log(foo); // foo здесь — функция
foo = '1'; // 3. присваивание перезаписывает функцию
Ключевые ко нцепции (Key Concepts)
1. Объявления функций полностью поднимаются
console.log(myFunc); // [Function: myFunc]
function myFunc() {
return 'Hello';
}
2. var поднимает только объявление, не присваивание
console.log(myVar); // undefined
var myVar = 'Hello';
3. Когда объявления функции и переменной имеют одинаковое имя
// Порядок hoisting
function foo() {} // функция поднимается и инициализируется первой
var foo; // объявление поднимается, но не перезаписывает функцию
// Поэтому foo по-прежнему функция
console.log(foo); // [Function: foo]
Полный порядок выполнения (Full Execution Flow)
// Исходный код
console.log(foo); // ?
var foo = '1';
function foo() {}
console.log(foo); // ?
// ======== Эквивалент ========
// Фаза создания (hoisting)
function foo() {} // 1️⃣ объявление функции полностью поднимается
var foo; // 2️⃣ объявление переменной поднимается, но не заменяет функцию
// Фаза выполнения
console.log(foo); // [Function: foo]
foo = '1'; // 3️⃣ присваивание теперь перезаписывает функцию
console.log(foo); // '1'
Расширенные вопросы (Extended Questions)
Вопрос A: меняет ли порядок результат?
console.log(foo); // ?
function foo() {}
var foo = '1';
console.log(foo); // ?
Ответ:
[Function: foo] // первый вывод
'1' // второй вывод
Причина: порядок в исходном коде не меняет приоритет hoisting. Объявление функции всё равно побеждает объявление переменной в фазе создания.
Вопрос B: несколько функций с одинаковым именем
console.log(foo); // ?
function foo() {
return 1;
}
var foo = '1';
function foo() {
return 2;
}
console.log(foo); // ?
Ответ:
[Function: foo] // первый вывод (позднее объявление функции переопределяет раннее)
'1' // второй вывод (присваивание перезаписывает функцию)
Причина:
// После hoisting
function foo() {
return 1;
} // первая функция
function foo() {
return 2;
} // вторая функция переопределяет первую
var foo; // только объявление (не перезаписывает функцию)
console.log(foo); // функция, возвращающая 2
foo = '1'; // присваивание перезаписывает функцию
console.log(foo); // '1'
Вопрос C: функциональное выражение vs объявление функции
console.log(foo); // ?
console.log(bar); // ?
var foo = function () {
return 1;
};
function bar() {
return 2;
}
Ответ:
undefined // foo — это undefined
[Function: bar] // bar — это функция
Причина:
// После hoisting
var foo; // объявление переменной поднимается (тело функции — нет)
function bar() {
return 2;
} // объявление функции полностью поднимается
console.log(foo); // undefined
console.log(bar); // function
foo = function () {
return 1;
}; // присваивание во время выполнения
Ключевое различие:
- Объявление функции:
function foo() {}-> полностью поднимается (включая тело) - Функциональное выражение:
var foo = function() {}-> поднимается только имя переменной
let/const избегают этого паттерна
// ❌ `var` имеет подводные камни с hoisting
console.log(foo); // undefined
var foo = '1';
// ✅ let/const находятся в TDZ до инициализации
console.log(bar); // ReferenceError
let bar = '1';
// ✅ использование одного имени с функцией и let/const вызывает ошибку
function baz() {}
let baz = '1'; // SyntaxError: Identifier 'baz' has already been declared
Сводка приоритетов Hoisting (Hoisting Priority Summary)
Приоритет hoisting (высокий -> низкий):
1. Объявление функции (Function Declaration)
- function foo() {} ✅ полностью поднимается
- наивысший приоритет
2. Объявление переменной (Variable Declaration)
- var foo ⚠️ только объявление (без присваивания)
- не перезаписывает существующее объявление функции
3. Присваивание переменной (Variable Assignment)
- foo = '1' ✅ может перезаписать функцию
- происходит только в фазе выполнения
4. Функциональное выражение (Function Expression)
- var foo = function() {} ⚠️ рассматривается как присваивание
- поднимается только имя переменной
Фокус на собеседовании (Interview Focus)
При ответе на этот тип вопросов можно следовать такой структуре:
- Объясните hoisting как две фазы: создание и выполнение.
- Подчеркните приоритет: объявление функции > объявление переменной.
- Перепишите код в поднятую форму для обоснования.
- Упомяните лучшие практики: предпочитайте
let/constи избегайте путаных паттернов сvar.
Пример ответа на собеседовании:
Этот вопрос о приоритете hoisting. В JavaScript объявления функций поднимаются перед объявлениями переменных.
Движок проходит две фазы:
- Фаза создания:
function foo() {}поднимается первым, затемvar fooподнимается без перезаписи функции.- Фаза выполнения:
console.log(foo)выводит функцию, и только позжеfoo = '1'перезаписывает её.На практике используйте
let/constвместоvar, чтобы избежать этой путаницы.