Vùng chết tạm thời (TDZ) trong JavaScript là gì?

Tôi biết Temporal Dead Zone nghe giống như một cụm từ khoa học viễn tưởng. Nhưng sẽ rất hữu ích nếu bạn hiểu các thuật ngữ và khái niệm bạn làm việc hàng ngày (hoặc muốn tìm hiểu) nghĩa là gì.

Cố gắng lên, vì điều này trở nên phức tạp.

Bạn có biết rằng trong JavaScript, chúng ta có thể thêm { }để thêm một mức phạm vi ở bất cứ đâu chúng ta muốn không?

Vì vậy, chúng tôi luôn có thể làm như sau:

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

Tôi đã bao gồm chi tiết này để đảm bảo rằng các ví dụ sắp tới có ý nghĩa (vì tôi không muốn cho rằng mọi người đều biết điều đó).

Trước ES6 không có cách nào khác để khai báo các biến ngoài var. Nhưng ES6 đã mang lại cho chúng tôi letconst.

letvà các constkhai báo đều thuộc phạm vi khối, có nghĩa là chúng chỉ có thể truy cập được trong phạm vi {}xung quanh chúng. var, mặt khác, không có hạn chế này.

Đây là một ví dụ:

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

Điều trên đã xảy ra bởi vì khai báo babyAgelại thành 2 chỉ khả dụng bên trong ifkhối. Ngoài ra, cái đầu tiên babyAgeđược sử dụng. Bạn có thể thấy rằng chúng là hai biến khác nhau không?

Ngược lại, varkhai báo không có phạm vi khối:

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

Sự khác biệt nổi bật cuối cùng giữa let/ constvarlà nếu bạn truy cập vartrước khi nó được khai báo, thì nó là không xác định. Nhưng nếu bạn làm tương tự cho letconst, họ ném một 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;

Họ ném lỗi tất cả vì Vùng chết tạm thời.

Giải thích về Vùng chết tạm thời

Đây là TDZ: thuật ngữ để mô tả trạng thái mà các biến không thể truy cập được. Chúng nằm trong phạm vi, nhưng chúng không được khai báo.

các letconstcác biến tồn tại trong TDZ từ khi bắt đầu phạm vi bao quanh cho đến khi chúng được khai báo.

Bạn cũng có thể nói rằng các biến tồn tại trong TDZ từ nơi chúng bị ràng buộc (khi biến bị ràng buộc với phạm vi bên trong của nó) cho đến khi nó được khai báo (khi một tên được dành riêng trong bộ nhớ cho biến đó).

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

Bạn có thể thấy ở trên rằng nếu tôi truy cập biến age sớm hơn so với khai báo của nó, nó sẽ ném ra một ReferenceError. Vì TDZ.

Nhưng varsẽ không làm điều đó. varchỉ được khởi tạo mặc định để undefinedkhông giống như khai báo khác.

Sự khác biệt giữa khai báo và khởi tạo là gì?

Đây là một ví dụ về khai báo một biến và khởi tạo một biến.

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

Khai báo một biến có nghĩa là chúng ta bảo lưu tên trong bộ nhớ ở phạm vi hiện tại. Đó là nhãn 1 trong các bình luận.

Khởi tạo một biến là thiết lập giá trị của biến. Đó là nhãn 2 trong các bình luận.

Hoặc bạn luôn có thể làm cả hai trên một dòng. Đó là nhãn 3 trong các bình luận.

Chỉ để lặp lại chính mình một lần nữa: letconstcác biến tồn tại trong TDZ từ khi bắt đầu phạm vi bao quanh cho đến khi chúng được khai báo.

Vậy từ đoạn mã trên, TDZ dùng cho ageđâu? Ngoài ra, handscó TDZ không? Nếu vậy, đâu là điểm bắt đầu và kết thúc của TDZ?

Kiểm tra câu trả lời của bạn Cả hai biến số tay và tuổi đều nhập vào TDZ.

TDZ cho các bàn tay kết thúc khi nó được khai báo, cùng một dòng mà nó được đặt thành 2.

TZ cho tuổi kết thúc khi nó được khai báo và tên được lưu trong bộ nhớ (ở bước 2, nơi tôi đã nhận xét).

Tại sao TDZ được tạo ra khi nó được?

Hãy quay lại ví dụ đầu tiên của chúng tôi:

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

Nếu chúng tôi thêm một console.logbên trong TDZ, bạn sẽ thấy lỗi này:

Tại sao TDZ lại tồn tại giữa phần đầu của phạm vi và phần khai báo biến? Lý do cụ thể cho điều đó là gì?

Đó là vì cẩu.

Công cụ JS đang phân tích cú pháp và thực thi mã của bạn có 2 bước để thực hiện:

  1. Phân tích cú pháp mã thành một cây cú pháp trừu tượng / mã byte thực thi, và
  2. Chạy thời gian thực hiện.

Bước 1 là nơi xảy ra quá trình lưu trữ và điều này được thực hiện bởi công cụ JS. Về cơ bản, nó sẽ di chuyển tất cả các khai báo biến của bạn lên đầu phạm vi của chúng. Vì vậy, một ví dụ sẽ là:

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

Để rõ ràng, các biến này không di chuyển vật lý trong mã. Tuy nhiên, kết quả sẽ giống về mặt chức năng như bên dưới:

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

Sự khác biệt duy nhất giữa constletlà khi chúng được kéo lên, giá trị của chúng không được đặt mặc định undefined.

Chỉ để chứng minh letconstcũng có thể nâng, đây là một ví dụ:

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

Đoạn mã trên là bằng chứng letrõ ràng ở trên nơi nó được khai báo, vì động cơ cảnh báo chúng ta về thực tế. Nó biết namenó tồn tại (nó được khai báo), nhưng chúng ta không thể truy cập nó trước khi nó được khởi tạo.

Nếu nó giúp bạn nhớ, hãy nghĩ về nó như thế này.

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.

Nó cũng cung cấp nhiều ngữ nghĩa hợp lý và được mong đợi hơn const(bởi vì constđược nâng lên, điều gì sẽ xảy ra nếu một lập trình viên cố gắng sử dụng nó trước khi nó được khai báo trong thời gian chạy? Biến nào nên giữ tại thời điểm nó được kéo lên?) Và là cách tiếp cận tốt nhất do nhóm thông số kỹ thuật ECMAScript quyết định.

Cách tránh các vấn đề mà TDZ gây ra

Tương đối đơn giản, hãy luôn đảm bảo rằng bạn xác định các lets và consts của mình ở đầu phạm vi của bạn.