1. var 聲明及變數提升(hoisting)機制 在函數作用域或全局作用域中通過關鍵字var聲明的變數,無論實際上是在哪裡聲明的,都會被當成在當前作用域頂部聲明的變數,這就是我們常說的提升機制。

如果你不熟悉js,可能會認為只有當str的值為true是才會創建變數value。

事實上,無論如何變數value都會創建。在預編譯階段,js引擎會將上面的getValue函數修改成下面這樣:

變數value的聲明被提升至函數頂部,而初始化操作依舊留在原處執行,這就意味著在else子句中也可以訪問到該變數,且由於此時變數尚未初始化,所以其值為undefined。

為此,ECMAScript 6 引入塊級作用域來強化對變數生命週期的控制。 2. 塊級聲明 塊級聲明用於聲明在指定塊的作用域之外無法訪問的變數。 塊級作用域(亦稱為詞法作用域)存在於:

  • 函數內部
  • 塊中(字元{和}之間的區域)

3. ES6 聲明變數的六種方法 ES5 -- var、function

ES6 -- var、function、let、const、import、class let 命令

ES6 新增了let命令,用來聲明變數。let聲明只在聲明所在的塊級作用域內有效

2. let聲明的變數不提升 js會把var的聲明和函數提到最前面。

用let命令聲明,不會發生變數提升。

var命令會發生」變數提升「現象,即變數可以在聲明之前使用,值為undefined。這種現象多多少少是有些奇怪的,按照一般的邏輯,變數應該在聲明語句之後纔可以使用。

為了糾正這種現象,let命令改變了語法行為,它所聲明的變數一定要在聲明後使用,否則報錯。

3. 不可重複聲明 let不允許在相同作用域內,重複聲明同一個變數。

也因此,不能在函數內部重新聲明參數。

但如果當前作用域內嵌另一個作用域,便可在內嵌的作用域中用let聲明同名變數,如:

4. 循環中的塊級作用域綁定

如果使用let,聲明的變數僅在塊級作用域內有效,最後輸出的是 6。

例子2

而現在es6 let和const提供的塊級綁定讓我們無須再這樣折騰

let聲明模仿上面的IIFE所做的一切來簡化循環過程。

對於for-in循環和for-of循環來說也是一樣的。

另外,for循環還有一個特別之處,就是設置循環變數的那部分是一個父作用域,而循環體內部是一個單獨的子作用域。

let 聲明在循環內部的行為是標準中專門定義的,它不一定與let的不提升特性相關。

事實上,早期的let實現不包含這一行為,它是後來加入的。 5. 暫時性死區(Temporal Dead Zone),簡稱 TDZ 只要塊級作用域內存在let命令,它所聲明的變數就「綁定」(binding)這個區域,不再受外部的影響。

存在全局變數tmp,但是塊級作用域內let又聲明瞭一個局部變數tmp,導致後者綁定這個塊級作用域,所以在let聲明變數前,對tmp賦值會報錯。 JavaScript引擎在掃描代碼發現變數聲明時,

要麼將它們提升至作用域頂部(遇到var聲明);

要麼從一開始就形成了封閉作用域,然後將聲明放到TDZ中(遇到let和const聲明),

訪問TDZ中的變數會觸發運行時錯誤(即在聲明之前就使用這些變數),

只有執行過變數聲明語句後,變數才會從TDZ中移出,然後方可正常訪問。

「暫時性死區」也意味著typeof不再是一個百分之百安全的操作。

變數a使用let命令聲明,所以在聲明之前,都屬於a的「死區」,只要用到該變數就會報錯。因此,typeof運行時就會拋出一個ReferenceError。 作為比較,如果一個變數根本沒有被聲明,使用typeof反而不會報錯。

typeof是在聲明變數a的代碼塊外執行的,此時a並不在TDZ中。這也就意味著不存在a這個綁定,typeoof操作最後返回undefined

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

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


推薦閱讀:
相關文章