Apa itu Zon Mati Temporal (TDZ) dalam JavaScript?

Saya tahu Temporal Dead Zone terdengar seperti frasa sci-fi. Tetapi sangat berguna untuk memahami maksud istilah dan konsep yang anda gunakan setiap hari (atau ingin belajar).

Ikutilah, kerana ini menjadi rumit.

Adakah anda sedar bahawa dalam JavaScript kita dapat menambah { }untuk menambahkan tahap skop di mana sahaja kita mahu?

Oleh itu, kami selalu dapat melakukan perkara berikut:

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

Saya telah memasukkan perincian ini untuk memastikan bahawa contoh yang akan datang masuk akal (kerana saya tidak mahu menganggap bahawa semua orang mengetahuinya).

Sebelum ES6 tidak ada cara lain untuk menyatakan pemboleh ubah selain var. Tetapi ES6 membawa kami letdan const.

letdan constdeklarasi keduanya dilindungi blok, yang bermaksud hanya boleh diakses di {}sekitarnya. var, sebaliknya, tidak mempunyai sekatan ini.

Inilah contohnya:

let babyAge = 1; let isBirthday = true; if (isBirthday) { let babyAge = 2; } console.log(babyAge); // Hmmmm. This prints 1

Perkara di atas berlaku kerana pengisytiharan semula babyAgeke 2 hanya tersedia di dalam ifblok. Di luar itu, yang pertama babyAgedigunakan. Bolehkah anda melihat bahawa mereka adalah dua pemboleh ubah yang berbeza?

Sebaliknya, vardeklarasi tersebut tidak mempunyai ruang lingkup blok:

var babyAge = 1; var isBirthday = true; if (isBirthday) { var babyAge = 2; } console.log(babyAge); // Ah! This prints 2

Perbezaan penting terakhir antara let/ constdan varadalah bahawa jika anda mengakses varsebelum dinyatakan, ia tidak ditentukan. Tetapi jika anda melakukan perkara yang sama letdan const, mereka membuang a 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;

Mereka membuang kesalahan kerana Zon Mati Temporal.

Zon Mati Temporal dijelaskan

Inilah yang dimaksudkan dengan TDZ: istilah untuk menggambarkan keadaan di mana pemboleh ubah tidak dapat dicapai. Mereka berada dalam ruang lingkup, tetapi tidak dinyatakan.

The letdanconstpemboleh ubah wujud dalam TDZ dari awal ruang lingkupnya sehingga mereka diisytiharkan.

Anda juga boleh mengatakan bahawa pemboleh ubah ada dalam TDZ dari tempat mereka terikat (ketika pemboleh ubah terikat dengan ruang lingkup yang ada di dalamnya) hingga dinyatakan (apabila nama dicadangkan dalam memori untuk pemboleh ubah tersebut).

{ // 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); }

Anda dapat melihat di atas bahawa jika saya mengakses pemboleh ubah usia lebih awal daripada perisytiharannya, ia akan membuang a ReferenceError. Kerana TDZ.

Tetapi vartidak akan berbuat demikian. varhanya lalai dimulakan untuk undefinedtidak seperti pengisytiharan lain.

Apakah perbezaan antara menyatakan dan memulakan?

Berikut adalah contoh menyatakan pemboleh ubah dan memulakan pemboleh ubah.

function scopeExample() { let age; // 1 age = 20; // 2 let hands = 2; // 3 }

Menyatakan pemboleh ubah bermaksud kita menyimpan nama dalam ingatan pada skop semasa. Itu dilabel 1 dalam komen.

Memulakan pemboleh ubah adalah menetapkan nilai pemboleh ubah. Itu dilabel 2 dalam komen.

Atau anda boleh melakukan kedua-duanya sekali sahaja. Itu dilabel 3 dalam komen.

Sekadar mengulangi diri saya: the letandconstpemboleh ubah wujud dalam TDZ dari awal ruang lingkupnya sehingga mereka diisytiharkan.

Oleh itu dari coretan kod di atas, untuk manakah TDZ itu age? Juga, adakah handsTDZ? Sekiranya demikian, di manakah permulaan dan akhir TDZ untuk tangan?

Periksa jawapan anda Pemboleh ubah tangan dan umur keduanya memasuki TDZ.

TDZ untuk tangan berakhir apabila dinyatakan, garis yang sama ia ditetapkan menjadi 2.

TZ untuk usia berakhir apabila dinyatakan, dan nama disimpan dalam ingatan (di langkah 2, tempat saya mengulas).

Mengapa TDZ dibuat semasa ia?

Mari kembali ke contoh pertama kami:

{ // 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); }

Sekiranya kita menambahkan bahagian console.logdalam TDZ, anda akan melihat ralat ini:

Mengapa TDZ wujud antara bahagian atas skop dan deklarasi pemboleh ubah? Apa alasan khusus untuk itu?

Ini kerana mengangkat.

Mesin JS yang menghurai dan melaksanakan kod anda mempunyai 2 langkah untuk dilakukan:

  1. Menghuraikan kod ke dalam Pohon Sintaks Abstrak / kod bait boleh laku, dan
  2. Jalankan pelaksanaan masa.

Langkah 1 adalah di mana pengangkatan berlaku, dan ini dilakukan oleh mesin JS. Ini pada dasarnya akan memindahkan semua pernyataan pemboleh ubah anda ke bahagian atas skopnya. Jadi contohnya ialah:

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

To be clear, these variables aren't physically moving in the code. But, the result would be functionally identical to the below:

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

The only difference between const and let is that when they are hoisted, their values don't get defaulted to undefined.

Just to prove let and const also hoist, here's an example:

{ // 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"; }

The above snippet is proof that let is clearly hoisted above where it was declared, as the engine alerts us to the fact. It knows name exists (it's declared), but we can't access it before it is initialized.

If it helps you to remember, think of it like this.

When variables get hoisted, var gets undefined initialized to its value by default in the process of hoisting. let and const also get hoisted, but don't get set to undefined when they get hoisted.

And that's the sole reason we have the TDZ. Which is why it happens with let and const but not var.

More examples of the TDZ

The TDZ can also be created for default function parameters. So something like this:

function createTDZ(a=b, b) { } createTDZ(undefined, 1); 

throws a ReferenceError, because the evaluation of variable a tries to access variable b before it has been parsed by the JS engine. The function arguments are all inside the TDZ until they are parsed.

Even something as simple as let tdzTest = tdzTest; would throw an error due to the TDZ. But var here would just create tdzTest and set it to undefined.

There's one more final and fairly advanced example from Erik Arvindson (who's involved in evolving and maintaining the ECMAScript spec):

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

You can follow the commented numbers.

In the first line we call the f function, and then try to access the b variable (which throws a ReferenceError because b is in the TDZ).

Why do we have the TDZ?

Dr Alex Rauschmayer has an excellent post on why the TDZ exists, and the main reason is this:

It helps us catch errors.

To try and access a variable before it is declared is the wrong way round, and shouldn't be possible.

It also gives more expected and rational semantics for const (because const is hoisted, what happens if a programmer tries to use it before it is declared at runtime? What variable should it hold at the point when it gets hoisted?), and was the best approach decided by the ECMAScript spec team.

How to avoid the issues the TDZ causes

Relatively simply, always make sure you define your lets and consts at the top of your scope.