儘管函數做用域是最多見的做用域單元,也是現行大多數javascript最廣泛的設計方法,但其餘類型的做用域單元也是存在的,而且經過使用其餘類型的做用域單元甚至能夠實現維護起來更加優秀、簡潔的代碼,好比塊做用域。隨着ES6的推廣,塊做用域也將用得愈來愈普遍。本文是深刻理解javascript做用域系列第四篇——塊做用域javascript
for (var i= 0; i<10; i++) { console.log(i); }
上面這段是很熟悉的循環代碼,一般是由於只想在for循環內部的上下文中使用變量i,但實際上i能夠在全局做用域中訪問,污染了整個做用域java
for (var i= 0; i<10; i++) { console.log(i); } console.log(i);//10
ES6改變了現狀,引入了新的let關鍵字,提供了除var之外的另外一種變量聲明方式。let關鍵字能夠將變量綁定到所在的任意做用域中(一般是{...}內部),實現塊做用域閉包
{ let i = 1; }; console.log(i);//ReferenceError: i is not defined
塊級做用域實際上能夠替代當即執行匿名函數(IIFE)ide
(function(){ var i = 1; })(); console.log(i);//ReferenceError: i is not defined
若是將文章最開始那段for循環的代碼中變量i用let聲明,將會避免做用域污染問題函數
for (let i= 0; i<10; i++) { console.log(i); } console.log(i);////ReferenceError: i is not defined
for循環頭部的let不只將i綁定到了for循環的塊中,事實上它將其從新綁定到了循環的每個迭代中,確保使用上一個循環迭代結束時的值從新進行賦值設計
循環ip
下面代碼中,因爲閉包只能取得包含函數中的任何變量的最後一個值,因此控制檯輸出5,而不是0作用域
固然,能夠經過函數傳參,來保存每次循環的值it
var a = []; for(var i = 0; i < 5; i++){ a[i] = (function(j){ return function(){ return j; } })(i); } console.log(a[0]());//0
而使用let則更方便,因爲let循環有一個從新賦值的過程,至關於保存了每一次循環時的值io
重複聲明
let不容許在相同做用域內,重複聲明同一個變量
{ let a = 10; var a = 1;//SyntaxError: Unexpected identifier }
{ let a = 10; let a = 1;//SyntaxError: Unexpected identifier }
提高
使用let進行的聲明不會在塊做用域中進行提高
{ console.log(i);//ReferenceError: i is not defined let i = 1; };
除了let之外,ES6還引入了const,一樣能夠用來建立塊做用域變量,但其值是固定的(常量)。以後任何試圖修改值的操做都會引發錯誤
if (true) { var a = 2; const b = 3; a = 3; b = 4;// TypeError: Assignment to constant variable } console.log( a ); // 3 console.log( b ); // ReferenceError: b is not defined
const聲明的常量,也與let同樣不可重複聲明
const message = "Goodbye!"; const message = "Goodbye!";//SyntaxError: Identifier 'message' has already been declared
try-catch語句的一個常見用途是建立塊級做用域,其中聲明的變量僅僅在catch內部有效
{ let a = 2; console.log(a); // 2 } console.log(a); //ReferenceError: a is not defined
在ES6以前的環境中,可使用try-catch語句達到上面代碼的相似效果
try{ throw 2; }catch(a){ console.log( a ); // 2 } console.log( a ); //ReferenceError: a is not defined