let 聲明會提高(hoist)嗎?

本文爲飢人谷講師方方原創文章,首發於 前端學習指南前端

前端日子我上課的時候跟飢人谷的學生講了《let 聲明的五個特色》,其中一個就是「let 聲明會提高到所在塊的頂部」,然而今天早上有個學生就問我了:git

MDN 上說 let 不會提高,爲何你說 let 會提高呢?

當時我內心一方:難道我講錯了?github

因而我看了 MDN 英文版的原文,發現寫的也是:面試

In ECMAScript 2015, let do not support Variable Hoisting, which means the declarations made using "let", do not move to the top of the execution block.

看來我真的錯了?因而我繼續翻看 ECMA-262.pdf,發現了兩處地方支持個人論點。微信

首先是 13.3.1 Let and Const Declarationside

let and const declarations define variables that are scoped to the running execution context's LexicalEnvironment. The variables are created when their containing Lexical Environment is instantiated but may not be accessed in any way until the variable's LexicalBinding is evaluated.

這說明即便是 block 最後一行的 let 聲明,也會影響 block 的第一行。這就是提高(hoisting)(這句話存疑)。函數

以及 18.2.1.2 Runtime Semantics: EvalDeclarationInstantiation( body, varEnv, lexEnv, strict)學習

The environment of with statements cannot contain any lexical declaration so it doesn't need to be checked for var/let hoisting conflicts.

這句話從側面證實了 let hoisting 的存在。 ui

ECMAScript 都提到了 var/let hoisting,我不知道還有什麼理由認爲 let hoisting 不存在。this

因此,我就把 MDN 的英文版和中文版給糾正過來了:(後來又被 TC39 的人改了)

在 ECMAScript 2015中, let 也會提高到語句塊的頂部。可是,在這個語句塊中,在變量聲明以前引用這個變量會致使一個 ReferenceError的結果

但願被以前 MDN 某個版本誤導的同窗周知。

總結一下:

  1. let 聲明會提高到塊頂部
  2. 從塊頂部到該變量的初始化語句,這塊區域叫作 TDZ(臨時死區)
  3. 若是你在 TDZ 內使用該變量,JS 就會報錯
  4. 我可沒說 TDZ 跟 hoisting 等價啊摔

更新:

有些同窗仍是認爲 let 不會提高,試試理解下面的代碼:

let a = 1
{
  a = 2
  let a
}

若是 let 不會提高,那麼 a = 2 就會將外面的 a 由 1 變成 2 啊。

運行發現 a = 2 報錯:Uncaught ReferenceError: a is not defined

這說明上面的代碼近似近似近似近似近似近似地能夠理解爲:(注意看註釋中的 TDZ)

let a = 1
{
  let a // TDZ 開始的地方就是這裏
  'start a TDZ'
  a = 2 // 因爲 a = 2 在 TDZ 中,因此報錯
  a // TDZ 結束的地方就是這裏
  'end a TDZ'
}

因此,let 提高了。可是因爲 TDZ 的存在,你不能在聲明以前使用這個變量。

更新2:

什麼是 hoisting?

我並無查到明確的定義,只能綜合理解一下。

JS 的 var 的 hoisting 最好理解:無論你把 var a 寫在函數的哪一行,都好像寫在第一行同樣;當前函數做用域裏的全部 a 都表示你寫的這個 a,這就是 hoisting。

如下是維基百科的釋義:

Variables are lexically scoped at function level (not block level as in C), and this does not depend on order ( forward declaration is not necessary): if a variable is declared inside a function (at any point, in any block), then inside the function, the name will resolve to that variable. This is equivalent in block scoping to variables being forward declared at the top of the function, and is referred to as hoisting.

什麼是 TDZ?

這個依然沒要找到明確的定義,大意是「在某個時間點以前,你不能訪問某個變量,即便這個變量已經存在了」。

JS 的 let 中這個「時間點」就是 LexicalBinding。

JS 的 let 中這個「存在」的意思是「The variables are created」。

至於 TDZ 裏這個變量有沒有被聲明,不是 TDZ 關心的。

個人結論:

  1. let 和 var 都有 hoisting
  2. let 有 TDZ,var 沒有 TDZ

不過說句實在話,let 有沒有 hoist 都無所謂,代碼還不是那樣寫。對 let 先使用再聲明的都是在耍流氓(面試官最喜歡刷流氓了)

感謝 @寸志 @鬍子大哈 @Code Hz 的補充。本文相關概念確實都沒有明肯定義,大部分是語言設計者造的概念,因此語言使用者理解起來有差別也是正常的

出現各類誤解的癥結也正是由於這些概念名沒有明確的定義。有興趣的話能夠看看 GitHub 上的討論:let hoisting? · Issue #767 · getify/You-Dont-Know-JS

我會在下一篇文章中詳細說明什麼是變量的生命週期,而後部分推翻我本身這篇文章的結論(打臉?)。(對啊,只記結論是沒有用的)

加微信號: astak10或者長按識別下方二維碼進入前端技術交流羣 ,暗號:寫代碼啦

每日一題,每週資源推薦,精彩博客推薦,工做、筆試、面試經驗交流解答,免費直播課,羣友輕分享... ,數不盡的福利免費送

相關文章
相關標籤/搜索