← Назад к вопросам

Что такое TDZ?

2.0 Middle🔥 71 комментариев
#Node.js и JavaScript

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

TDZ (Temporal Dead Zone) в JavaScript

TDZ — это зона в коде, где переменная объявлена, но еще не инициализирована. Это важная концепция в современном JavaScript, которая часто путает разработчиков, особенно тех, кто привык к var.

Что такое TDZ?

TDZ (Temporal Dead Zone) — это период между началом выполнения блока scope и момента, когда переменная инициализируется.

В этот период попытка доступа к переменной вызовет ReferenceError, даже если она объявлена ниже:

// TDZ начинается здесь
console.log(x);  // ReferenceError: Cannot access 'x' before initialization
let x = 5;       // TDZ заканчивается здесь

Это отличается от поведения var, которая будет undefined:

console.log(y);  // undefined (hoisting)
var y = 5;       // инициализация

Почему существует TDZ?

Главная причина: гарантировать явную инициализацию перед использованием. Это помогает ловить ошибки на ранних этапах.

// С let/const код ЯСЕН и БЕЗОПАСЕН
const MAX_USERS = 100;  // Явно инициализирована
function getMaxUsers() {
  return MAX_USERS;  // Безопасно — всегда инициализирована
}

// С var это загадка
if (someCondition) {
  var MAX_USERS = 100;
}
console.log(MAX_USERS);  // undefined или 100? Непредсказуемо!

Как работает TDZ подробно

Этап 1: Создание scope (Creation phase)

{
  // TDZ начинается с открытия блока
  // x существует в памяти, но не инициализирована
  // typeof x;  // ReferenceError!
  
  let x;
  // TDZ заканчивается здесь
  // Теперь x существует и равна undefined
}

Этап 2: Использование переменной (Execution phase)

function example() {
  // x находится в TDZ
  console.log(x);  // ReferenceError
  
  let x = 5;  // x выходит из TDZ, инициализируется
  console.log(x);  // 5 ✓
}

TDZ с let vs const vs var

let и const подвержены TDZ:

// let
function testLet() {
  console.log(x);  // ReferenceError: Cannot access 'x' before initialization
  let x = 1;
}

// const — то же самое
function testConst() {
  console.log(y);  // ReferenceError: Cannot access 'y' before initialization
  const y = 1;
}

var НЕ подвержена TDZ (hoisting):

function testVar() {
  console.log(z);  // undefined (не ошибка!)
  var z = 1;
}

// На самом деле это эквивалентно:
function testVar() {
  var z;           // объявление хойстится
  console.log(z);  // undefined
  z = 1;           // инициализация
}

Практические примеры, где TDZ ловит ошибки

Пример 1: Случайный порядок кода

// Плохой код — работает с var, но хаотичен
var x = 5;
console.log(x);  // 5

// Случайный разработчик может добавить var выше, и он не заметит
if (condition) {
  var x = 10;  // Перепишет глобальный x
}

// С let это будет ошибка (хорошо!):
let x = 5;
if (condition) {
  let x = 10;  // Новая переменная в блок scope, не переписываем
}
console.log(x);  // 5 (не 10)

Пример 2: Забыли инициализировать

// Ошибка поймана сразу
try {
  console.log(user);  // ReferenceError
  const user = getUserFromDB();
} catch (e) {
  // Код явно ломается, видим ошибку
}

Пример 3: Циклические зависимости в модулях

// module-a.js
import { funcB } from './module-b';

export function funcA() {
  funcB();  // Может вызвать ReferenceError если funcB использует переменную из А
}

// module-b.js
import { funcA } from './module-a';

export function funcB() {
  console.log(someVar);  // ReferenceError если someVar еще не инициализирована
}

const someVar = 'test';

TDZ в блок-scope

ТДЗ существует для каждого блока scope:

function example() {
  let x = 1;
  
  if (true) {
    // В этом блоке новый TDZ для x
    console.log(x);  // ReferenceError (не видит x из внешнего scope)
    let x = 2;
  }
  
  console.log(x);  // 1 (внешний x)
}

TDZ с функциями

Function declarations НЕ подвержены TDZ (полностью hoisted):

console.log(func);  // [Function: func]
func();             // Работает!

function func() {
  return 'hello';
}

Function expressions подвержены TDZ:

console.log(arrow);  // ReferenceError
const arrow = () => 'hello';

// Эквивалентно var:
console.log(func);  // undefined
var func = () => 'hello';

TDZ с class

Classes подвержены TDZ:

new MyClass();  // ReferenceError: Cannot access 'MyClass' before initialization

class MyClass {
  constructor() {}
}

typeof в TDZ

Обычно typeof не выбрасывает ошибку:

console.log(typeof undefinedVar);  // 'undefined' (переменная не существует)

Но если переменная в TDZ, она выбросит ошибку:

console.log(typeof x);  // ReferenceError (x находится в TDZ)
let x = 5;

Это потому что JavaScript знает о переменной (через parsing), но она не инициализирована.

Визуализация TDZ

{
  // ├─ TDZ для x начинается
  // │
  // ├─ x = undefined (временно недоступна)
  // │
  console.log(x);  // ← ReferenceError (находимся в TDZ)
  // │
  let x = 5;  // ← TDZ заканчивается, x инициализируется
  // │
  console.log(x);  // ← 5 (TDZ пройдена)
  // │
} // ├─ x выходит из scope

Лучшие практики

1. Объявляйте переменные в начале блока

// Хорошо
function doSomething() {
  const config = getConfig();
  const data = getData();
  
  process(config, data);
}

// Плохо (может вызвать ReferenceError если объявить ниже)
function doSomething() {
  process(config, data);
  
  const config = getConfig();
  const data = getData();
}

2. Используйте const по умолчанию

// const не только защищает от переопределения
// Но и делает намерение кода ясным
const API_KEY = process.env.API_KEY;  // Не изменяется
let counter = 0;  // Изменяется

3. Используйте linter (ESLint)

{
  "rules": {
    "no-use-before-define": "error",
    "prefer-const": "warn"
  }
}

Заключение

TDZ — это защитный механизм JavaScript для:

  • Предотвращения ошибок с неинициализированными переменными
  • Гарантирования более предсказуемого поведения
  • Помощи в написании безопаснее кода

Хотя TDZ может выглядеть как ограничение, она на самом деле защищает разработчиков от целого класса ошибок, которые были возможны с var. Понимание TDZ критично для работы с modern JavaScript.