ES6之塊級作用域
作用域
作用域指變數所作用的範圍,在 Javascript 中有兩種作用域:
全局作用域
函數作用域
變數提升
變數提升(Hoisting)被認為是, Javascript 中執行上下文 (特別是創建和執行階段)工作方式的一種認識。具體表現就是所有通過 var 聲明的變數會提升到當前作用域的最前面。
可以看到用 var 聲明了的並不會報錯。因為其實函數 bar 等同於
大多數類 C 語言語法的語言都擁有塊級作用域。在一個代碼塊(括在一對花括弧中的一組語句)中定義的所有變數在代碼塊的外部是不可見的。定義在代碼塊中的變數在代碼塊被執行結束後會變釋放掉。這是件好事。
糟糕的是,儘管 Javascript 的代碼貌似支持塊級作用域,但實際上 Javascript 並不支持(就是因為有變數提升)。這個混淆之處可能成為錯誤之源。
所以在 ES6 中規定了 let 和 const 來支持塊級作用域。但是,是不是真的提升就不存在了呢,可以看下面暫時性死區這部分。
let
let 可以理解為『更完美的 var』,使用方法很簡單;
使用方法基本和 var 相同,而且聲明的變數只在其塊和子塊中可用,這點也與 var 相同。 二者之間最主要的區別在於 var 聲明的變數的作用域是整個封閉函數。
let 聲明的變數的作用域只是外層塊,而不是整個外層函數。
我們可以利用這個特性來替代立即執行函數(IIFE)。
const
const 的用法跟 let 差不多,但是 const 一定要初始化, 不初始化是會報錯的。
const 是塊級作用域,const 跟 let 的語義相似,就是用來聲明常量的,一旦聲明了就不能更改。值得注意的是 const 聲明的變數記錄的是指針,不可更改的是指針,如果 const 所聲明的是對象,對象的內容還是可以修改的。
暫時性死區
使用 let 或 const 聲明的變數,在聲明沒有到達之前,訪問該變數都會導致報錯,就連一直以為安全的 typeof 也不再安全。
報的錯是 ReferenceError,如果使用 var 聲明的話,temp 輸出應該是 undefined,從 let 聲明的變數的塊的第一行,到聲明變數之間的這個區域被稱作暫時性死區(TDZ)。凡是在這個區域使用這些變數都會報錯。
看到上面兩個例子仔細思考有沒有覺得想到點什麼?
在函數里沒有用 let 聲明 temp 的時候,temp 是 undefined,講道理在 let 聲明前也應該是 temp,然而 foo 函數卻報了錯,證明了就算是在未到達 let 聲明的地方,但是在用 let 之前已經起到了作用。這是不是說明其實 let 也有提升,只是在 TDZ 使用的時候報錯了,而不是 undefined。
事實上,當 JS 引擎檢視下面的代碼塊有變數聲明時,對於 var 聲明的變數,會將聲明提升到函數或全局作用域的頂部,而對 let 或 const 的時候會將聲明放在暫時性死區內。任何在暫時性死區內訪問變數的企圖都會導致「運行時」錯誤(runtime error)。只有執行到變數的聲明語句時,該變數才會從暫時性死區內被移除並可以安全使用。
禁止重複聲明
在同一個塊內,let 和 const 不能聲明相同的標識符。禁止的情況包括:
let 或 const 和 let 或 const
var 和 let 或者 const
函數參數與 let 或 const
以上情況都是會報 SyntaxError。但是在嵌套的作用域內使用 let 聲明同一變數是被允許的。
同時因為是 let 和 const 是塊級作用域,聲明的變數在當前塊使用完之後就會被釋放,所以就算使用相同的標識符也不會覆蓋外部作用域的變數, 而 var 是會覆蓋外部作用域的變數的。
最佳實踐
在 ES6 的發展階段,被廣泛認可的變數聲明方式是:默認情況下應當使用 let 而不是 var 。對於多數 JS 開發者來說, let 的行為方式正是 var 本應有的方式,因此直接用 let
替代 var 更符合邏輯。在這種情況下,你應當對需要受到保護的變數使用 const。
在默認情況下使用 const ,而只在你知道變數值需要被更改的情況下才使用 let 。這在代碼中能確保基本層次的不可變性,有助於防止某些類型的錯誤。
思考題
TAG:前端桃園 |