理解ES6中的TDZ(暫時性死區)

筱筱醬發表於2019-01-05

什麼是TDZ

Temporal Dead Zone(TDZ)是ES6(ES2015)中對作用域新的專用語義。TDZ名詞並沒有明確地寫在ES6的標準檔案中,一開始是出現在ES Discussion討論區中,是對於某些遇到在區塊作用域繫結早於宣告語句時的狀況時,所使用的專用術語。

近來在專案中遇到一個問題然後才意識到這個TDZ,然後再次拜讀了阮一峰老是的ES6詳解啊,做出了一些自己的筆記,方便下次review啊,

let/const和var的區別

在ES6之前,JS的scope只有兩種,全域性作用域和函式作用域,但是在ES6種出現了塊級作用域,即使用let/const可以定義塊級作用域。 那麼在ES6的新特性中,最容易看到TDZ作用的就是使用let/const的使用上面。 let/const與var的主要不同有兩個地方:

  • let/const是使用區塊作用域;var是使用函式作用域
  • 在let/const宣告之前就訪問對應的變數與常量,會丟擲ReferenceError錯誤;但在var宣告之前就訪問對應的變數,則會得到undefined
console.log(Vname); // undefined;
console.log(Lname); // ReferenceError
var Vname = 'xiaoxiao';
let Lname = 'xiaoxiao';
複製程式碼

實踐證明當我們在未宣告之前使用var定義的變數時會得到undefined,但是在使用let未定義的變數時會丟擲錯誤。因為ES6中的let宣告的變數是不存在變數提升的作用。

var x = 10;
if (true) {
    x = 20; // ReferenceError
    let x;
 }
複製程式碼

ES6 明確規定,如果區塊中存在let和const命令,這個區塊對這些命令宣告的變數,從一開始就形成了封閉作用域。凡是在宣告之前就使用這些變數,就會報錯。 總之,在程式碼塊內,使用let命令宣告變數之前,該變數都是不可用的。這在語法上,稱為“暫時性死區”(temporal dead zone,簡稱 TDZ)。

if (true) {
  // TDZ開始
  tmp = 'abc'; // ReferenceError
  console.log(tmp); // ReferenceError

  let tmp; // TDZ結束
  console.log(tmp); // undefined

  tmp = 123;
  console.log(tmp); // 123
}
複製程式碼

上面程式碼中,在let命令宣告變數tmp之前,都屬於變數tmp的“死區”。

typeof的“死區”陷阱

我們都知道使用typeof 可以用來檢測給定變數的資料型別,也可以使用它來判斷值是否被定義。當返回undefined時表示值未定義; 但是在const/let定義的變數在變數宣告之前如果使用了typeof就會報錯

typeof x; // ReferenceError
let x;
複製程式碼

因為x是使用let宣告的,那麼在x未宣告之前都屬於暫時性死區,在使用typeof時就會報錯。所以在使用let/const進行宣告的變數在使用typeof時不一定安全喔。

typeof y; // 'undefined'
複製程式碼

但是可以看出,如果我們只是用了typeof而沒有去定義,也是不會報錯的,從這粒可以看出只要沒有明確規定使用const/let定義之前就是不會出錯。

傳參的“死區”陷阱

例如下面一段程式碼,我們在使用

function bar(x = y, y = 2) {
  return [x, y];
}

bar(); // 報錯
複製程式碼

上面程式碼中,呼叫bar函式之所以報錯(某些實現可能不報錯),是因為引數x預設值等於另一個引數y,而此時y還沒有宣告,屬於”死區“。如果y的預設值是x,就不會報錯,因為此時x已經宣告瞭。

function bar(x = 2, y = x) {
  return [x, y];
}
bar(); // [2, 2]
複製程式碼

使用var和let宣告的另外一種區別。

// 不報錯
var x = x;

// 報錯
let x = x;
// ReferenceError: x is not defined
複製程式碼

受“死區”的影響,使用let宣告變數時,只要變數在還沒有宣告完成前使用,就會報錯。上面這行就屬於這個情況,在變數x的宣告語句還沒有執行完成前,就去取x的值,導致報錯”x 未定義“。

總結

ES6 規定暫時性死區和let、const語句不出現變數提升,主要是為了減少執行時錯誤,防止在變數宣告前就使用這個變數,從而導致意料之外的行為。這樣的錯誤在 ES5 是很常見的,現在有了這種規定,避免此類錯誤就很容易了。

總之,暫時性死區的本質就是,只要一進入當前作用域,所要使用的變數就已經存在了,但是不可獲取,只有等到宣告變數的那一行程式碼出現,才可以獲取和使用該變數。

注: TDZ最一開始是為了const所設計的,但後來的對let的設計也是一致的。

注: 在ES6標準中,對於const所宣告的識別子仍然也經常為variable(變數),稱為constant variable(固定的變數)。以const宣告所建立出來的常量,在JS中只是不能再被賦(can't re-assignment),並不是不可被改變(immutable)的,這兩種概念仍然有很大的差異。

歡迎關注公眾號

理解ES6中的TDZ(暫時性死區)

相關文章