前面的話
盡管函數作用域是最常見的作用域單元,也是現行大多數javascript最普遍的設計方法,但其他類型的作用域單元也是存在的,并且通過使用其他類型的作用域單元甚至可以實現維護起來更加優秀、簡潔的代碼,比如塊作用域。隨著ES6的推廣,塊作用域也將用得越來越廣泛。本文是深入理解javascript作用域系列第四篇——塊作用域
let
for (var i= 0; i<10; i++) { console.log(i); }
上面這段是很熟悉的循環代碼,通常是因為只想在for循環內部的上下文中使用變量i,但實際上i可以在全局作用域中訪問,污染了整個作用域
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)
(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循環的塊中,事實上它將其重新綁定到了循環的每一個迭代中,確保使用上一個循環迭代結束時的值重新進行賦值
//與上一段代碼等價 { let j; for (j=0; j<10; j++) { let i = j; //每個迭代重新綁定 console.log( i ); } }
循環
下面代碼中,由于閉包只能取得包含函數中的任何變量的最后一個值,所以控制臺輸出5,而不是0
var a = []; for(var i = 0; i < 5; i++){ a[i] = function(){ return i; } } console.log(a[0]());//5
當然,可以通過函數傳參,來保存每次循環的值
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循環有一個重新賦值的過程,相當于保存了每一次循環時的值
var a = []; for(let i = 0; i < 5; i++){ a[i] = function(){ return i; } } console.log(a[0]());//0
重復聲明
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; };
const
除了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
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
//或者 try{ throw undefined; }catch(a){ a = 2; console.log( a ); // 2 } console.log( a ); //ReferenceError: a is not defined
文章列表