Рубрики
Без рубрики

Что такое Temporal Dead Zone (TDZ) в JavaScript? | Области видимости JavaScript

Что такое временная мертвая зона (TDZ) в JavaScript? Области видимости переменных JavaScript.

Я знаю, что Temporal Dead Zone звучит как научно-фантастическая фраза. Но полезно понимать, что означают термины и понятия, с которыми вы работаете каждый день (или хотите узнать о них).

Пристегнитесь, потому что дальше будет сложнее.

Знаете ли вы, что в JavaScript мы можем добавлять { }, чтобы добавить уровень области видимости, куда захотим?

Поэтому мы всегда можем сделать следующее:

{ { { { { { var madness = true } } } } } }

Надеюсь, вы не увидите этого в производственном коде!

Я включил эту деталь, чтобы убедиться, что предстоящие примеры имеют смысл (поскольку я не хотел предполагать, что все это знают).

До появления ES6 не существовало другого способа объявления переменных, кроме var. Но ES6 принес нам let и const.

Объявления let и const имеют блочное копирование, что означает, что они доступны только в пределах { }, окружающих их. var, с другой стороны, не имеет такого ограничения.

Вот пример:

let babyAge = 1;
let isBirthday = true;

if (isBirthday) {
	let babyAge = 2; 
}

console.log(babyAge); // Hmmmm. This prints 1

Это произошло потому, что повторное объявление babyAge равным 2 доступно только внутри блока if. После этого используется первый babyAge. Вы видите, что это две разные переменные?

В отличие от этого, объявление var не имеет области видимости блока:

var babyAge = 1;
var isBirthday = true;

if (isBirthday) {
	var babyAge = 2; 
}

console.log(babyAge); // Ah! This prints 2

Последнее существенное различие между let / const и var заключается в том, что если вы обращаетесь к var до того, как она была объявлена, она становится неопределенной. Но если вы сделаете то же самое для let и const, они выдадут ошибку ReferenceError.

console.log(varNumber); // undefined
console.log(letNumber); // Doesn't log, as it throws a ReferenceError letNumber is not defined

var varNumber = 1;
let letNumber = 1;

Ошибка возникает из-за Temporal Dead Zone.

Объяснение Temporal Dead Zone

Вот что такое TDZ: термин для описания состояния, когда переменные недоступны. Они находятся в области видимости, но не объявлены.

Переменные let и const существуют в TDZ с начала их объемлющей области видимости до момента их объявления.

Можно также сказать, что переменные существуют в TDZ с момента их привязки (когда переменная привязывается к области видимости, внутри которой она находится) до момента ее объявления (когда для этой переменной в памяти резервируется имя).

{
 	// This is the temporal dead zone for the age variable!
	// This is the temporal dead zone for the age variable!
	// This is the temporal dead zone for the age variable!
 	// This is the temporal dead zone for the age variable!
	let age = 25; // Whew, we got there! No more TDZ
	console.log(age);
}

Вы можете видеть выше, что если бы я обратился к переменной age раньше, чем она была объявлена, это вызвало бы ошибку ReferenceError. Из-за TDZ.

Но var этого не сделает. var просто инициализируется по умолчанию в undefined, в отличие от другого объявления.

В чем разница между объявлением и инициализацией?

Вот пример объявления переменной и инициализации переменной.

function scopeExample() {

    let age; // 1
    age = 20; // 2
    let hands = 2; // 3
}

Объявление переменной означает, что мы резервируем ее имя в памяти в текущей области видимости. В комментариях это обозначено 1.

Инициализация переменной – это установка значения переменной. Это обозначено 2 в комментариях.

Или вы всегда можете сделать и то, и другое в одной строке. Это обозначено 3 в комментариях.

Повторю еще раз: переменные let и const существуют в TDZ с начала их объемлющей области видимости до момента их объявления.

Итак, из приведенного выше фрагмента кода, где находится TDZ для age? Также, есть ли у hands TDZ? Если да, то где начало и конец TDZ для hands?

Почему TDZ создается именно тогда, когда она создается?

Давайте вернемся к нашему первому примеру:

{
    // This is the temporal dead zone for the age variable!
    // This is the temporal dead zone for the age variable!
    // This is the temporal dead zone for the age variable!
    // This is the temporal dead zone for the age variable!
    let age = 25; // Whew, we got there! No more TDZ
    console.log(age);
}

Если мы добавим console.log внутри TDZ, вы увидите эту ошибку:

Почему TDZ существует между вершиной области видимости и объявлением переменной? Какова конкретная причина для этого?

Это связано с hoisting.

JS-движок, который разбирает и выполняет ваш код, должен сделать 2 шага:

  • Парсинг кода в абстрактное синтаксическое дерево/исполняемый байт-код, и
  • выполнение во время исполнения.

На шаге 1 происходит подъем, который выполняется движком JS. По сути, он перемещает все объявления переменных в верхнюю часть их области видимости. Примером может быть:

console.log(hoistedVariable); // undefined
var hoistedVariable = 1;

Чтобы было понятно, эти переменные физически не перемещаются в коде. Но результат будет функционально идентичен приведенному ниже:

var hoistedVariable;

console.log(hoistedVariable); // undefined
counter = 1;

Единственное различие между const и let заключается в том, что когда они поднимаются, их значения не становятся по умолчанию неопределенными.

Чтобы доказать, что let и const также поднимаются, приведем пример:

{
    // Both the below variables will be hoisted to the top of their scope!
	console.log(typeof nonsenseThatDoesntExist); // Prints undefined
	console.log(typeof name); // Throws an error, cannot access 'name' before initialization

	let name = "Kealan";
}

Приведенный выше фрагмент является доказательством того, что let явно поднят выше того места, где он был объявлен, поскольку движок предупреждает нас об этом. Он знает, что имя существует (оно объявлено), но мы не можем получить к нему доступ до его инициализации.

Если это поможет вам запомнить, думайте об этом так.

Когда переменные подхватываются, var получает значение undefined, инициализированное по умолчанию в процессе подхвата. let и const также подхватываются, но не устанавливаются в значение undefined, когда они подхватываются.

И это единственная причина, по которой у нас есть TDZ. Вот почему это происходит с let и const, но не с var.

Другие примеры TDZ

TDZ также может быть создан для параметров функции по умолчанию. Например:

function createTDZ(a=b, b) {
}

createTDZ(undefined, 1); 

выбрасывает ошибку ReferenceError, поскольку при оценке переменной a происходит попытка доступа к переменной b до того, как она была разобрана движком JS. Все аргументы функции находятся внутри TDZ, пока они не будут разобраны.

Даже что-то простое, как let tdzTest = tdzTest; вызовет ошибку из-за TDZ. Но var здесь просто создаст tdzTest и установит его в неопределенное значение.

Есть еще один последний и довольно продвинутый пример от Эрика Арвиндсона (который участвует в развитии и поддержке спецификации ECMAScript):

let a = f(); // 1
const b = 2;
function f() { return b; } // 2, b is in the TDZ

Вы можете проследить за закомментированными цифрами.

В первой строке мы вызываем функцию f, а затем пытаемся получить доступ к переменной b (что вызывает ошибку ReferenceError, поскольку b находится в TDZ).

Почему у нас есть TDZ?

Доктор Алекс Раушмайер написал отличный пост о том, зачем существует TDZ, и главная причина заключается в следующем:

Она помогает нам отлавливать ошибки.

Пытаться получить доступ к переменной до ее объявления – это неправильный путь, и он не должен быть возможным.

Кроме того, это дает более ожидаемую и рациональную семантику для const (поскольку const поднята, что произойдет, если программист попытается использовать ее до того, как она будет объявлена во время выполнения? Какую переменную она должна держать в момент, когда ее поднимают?), и это был лучший подход, который решила команда разработчиков спецификации ECMAScript.

Как избежать проблем, вызываемых TDZ

Относительно просто: всегда убеждайтесь, что вы определяете свои lets и consts в верхней части области видимости.

Оригинал: “https://www.freecodecamp.org/news/what-is-the-temporal-dead-zone/”