Hoisting и области видимости в JavaScript: var, let, const и TDZ простыми словами
Если вы когда-нибудь видели, как переменная оказывается «доступной» до места своего объявления, вы сталкивались с hoisting (поднятием). В этой статье простыми словами разберём hoisting в JavaScript, области видимости (глобальная, функциональная и блочная), var против let/const, TDZ, поведение функций и типичные ловушки. Материал ориентирован на запрос «hoisting в JavaScript простыми словами» и поможет уверенно писать код без неожиданных ошибок.
Что такое hoisting (поднятие)?
Hoisting — это процесс, при котором объявления переменных и функций как бы «перемещаются» интерпретатором в верхнюю часть своей области видимости на этапе компиляции. Важно: поднимаются объявления, а не инициализации.
console.log(a); // undefined (объявление var поднято, значение ещё не присвоено)
var a = 10;
console.log(foo()); // "ok" — объявления функций (function declaration) поднимаются целиком
function foo() { return "ok"; }
console.log(typeof bar); // "undefined" — bar есть как переменная, но ещё без значения
try {
bar();
} catch (e) {
console.log(e.name); // TypeError: bar не функция (пока undefined)
}
var bar = function () { return "bar"; };
Объявления функций (function declaration) поднимаются вместе с телом, а функциональные выражения (function expression) — нет: поднимается только переменная (при var) со значением undefined до инициализации.
var, let, const и блочная область видимости
Ключевое отличие: var — функциональная область видимости, а let/const — блочная. Это влияет на доступность переменных внутри if, for, while и т. п.
if (true) {
var x = 1;
let y = 2;
const z = 3;
}
console.log(x); // 1 — var «протёк» наружу (функциональная область)
console.log(typeof y); // ReferenceError — y виден только внутри блока
console.log(typeof z); // ReferenceError — z тоже только внутри блока
Дополнительно: в браузере глобальный var попадает на window, а глобальные let/const — нет.
// В браузере:
var g1 = 1;
let g2 = 2;
console.log(window.g1); // 1
console.log(window.g2); // undefined
TDZ: Temporal Dead Zone — почему let/const «не доступны» до объявления
let и const тоже поднимаются, но попадают в зону временной мёртвости (TDZ), пока не достигнута строка объявления. Любой доступ до объявления бросит ReferenceError, даже typeof!
try {
console.log(b);
} catch (e) {
console.log(e.name); // ReferenceError
}
let b = 5;
try {
console.log(typeof c);
} catch (e) {
console.log(e.name); // ReferenceError — даже typeof не спасает в TDZ
}
let c;
Функциональные выражения на let/const тоже подчиняются TDZ:
try {
baz();
} catch (e) {
console.log(e.name); // ReferenceError
}
let baz = () => "baz";
for и замыкания: почему var ломает таймеры
Классическая ловушка с var в цикле: все колбэки «видят» одно и то же i, которое к моменту выполнения уже изменилось.
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0);
}
// 3, 3, 3
for (let j = 0; j < 3; j++) {
setTimeout(() => console.log(j), 0);
}
// 0, 1, 2 — у let своя блочная область на каждую итерацию
Исторический обходной путь с IIFE для var:
for (var i = 0; i < 3; i++) {
(function(i) {
setTimeout(() => console.log(i), 0);
})(i);
}
Функции: объявления против выражений
Ещё раз кратко: function declaration поднимается целиком и может вызываться до места записи; function expression — нет.
greet(); // работает
function greet() { console.log("Привет!"); }
try {
speak();
} catch (e) {
console.log(e.name); // TypeError или ReferenceError в зависимости от var/let
}
var speak = function() { console.log("Говорю"); };
Строгий режим и утечки глобальных переменных
Hoisting не отключается строгим режимом, но strict-mode блокирует неявное создание глобалок присваиванием без объявления — частый друг багов с поднятием.
function demo() {
'use strict';
try {
ghost = 42; // без объявления
} catch (e) {
console.log(e.name); // ReferenceError
}
}
demo();
Типичные ошибки и как их избежать
- Использование переменной до объявления: включите правило ESLint no-use-before-define и старайтесь объявлять переменные ближе к месту использования.
- var в циклах и блоках: отдавайте приоритет let и const — они блочно-областные и безопаснее.
- Переопределение: var позволяет повторно объявлять переменные в одной области — ловушка для отладки. let/const такого не допускают.
- const не означает «неизменяемый объект»: нельзя переassign’ить ссылку, но менять свойства можно.
const user = { name: "Ann" };
user.name = "Ben"; // ок
// user = {} ; // TypeError — переназначить нельзя
Рекомендации по стилю и архитектуре
- По умолчанию используйте const; переходите на let, когда действительно нужна переназначаемая переменная.
- Избегайте var в современном коде. Он нужен только для совместимости со старым кодом или специфическими средами.
- Не полагайтесь на hoisting для «магии». Объявляйте функции и переменные до использования — код станет предсказуемее.
- Включайте ESLint: no-use-before-define, prefer-const, block-scoped-var помогут отловить ошибки ранней стадии.
Мини-чеклист по hoisting в JavaScript
- var — функциональная область; объявления поднимаются как undefined.
- let/const — блочная область; есть TDZ до строки объявления (ReferenceError).
- Function declaration поднимается целиком; function expression — нет.
- Для циклов и асинхронных колбэков используйте let, чтобы избежать «общего» индекса.
- Строгий режим не убирает hoisting, но предотвращает неявные глобальные переменные.
Хотите быстро закрепить тему на практике и закрыть пробелы в основах и продвинутых приёмах? Рекомендую пройти практический курс «JavaScript с Нуля до Гуру 2.0» — прокачайте навыки на реальных задачах.
Итоги
Понимание hoisting и областей видимости — фундамент для уверенного JavaScript. Освойте разницу между var, let и const, не забывайте про TDZ, аккуратно используйте функции и держите код линтимым. Так вы избежите самых коварных багов и ускорите разработку в реальных проектах.
-
Создано 18.03.2026 17:05:05
-
Михаил Русаков

Комментарии (0):
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.