1. 程式人生 > >塊級作用域綁定

塊級作用域綁定

對象創建 數字 函數 null on() 塊級作用域 true 即使 函數表

#var聲明及變量提升機制 在 函數作用域 或 全局作用域中通過 var 聲明的變量,無論實際上在哪裏聲明的,都會被當成在 當前作用域頂部聲明的變量。這就是常說的提升機制;function func(condition){ if(condition){ var value="xxx"; return value; } else{ //這裏可以訪問 value,其值為 undefined return null; } //此處可以訪問 value,其值為 undefined} 上面的代碼,在預編譯階段,JavaScript引擎會將上面的func函數修改成下面這樣:
function func(condition){ var value; if(condition){ value="xxx"; return value; } else{ return null; }} 變量 value 的聲明被提升至函數頂部,而初始化操作依舊在原處執行,這就意味著在 else 子句中可以訪問到該變量,且由於沒有被初始化,所以其值為 undefined; 正因為這個問題,ECMAScript 6 引入了塊級作用域來強化對變量聲明周期的控制

#塊級聲明 塊級聲明用於聲明在指定塊的作用域之外無法訪問的變量。塊級作用域存在於:
1,函數內部; 2,塊中(花括號{}之間); &let聲明 let用法和var相同。用let來替代var來聲明變量,可以把變量的作用域限制在當前代碼塊中。function func(condition){ if(condition){ let value="xxx"; return value; } else{ //變量 value 在此處不存在 return null; } //變量 value 在此處不存在} 變量 value 用let聲明後,不再被提升至函數頂部。執行流離開if塊,value 立刻被銷毀。如果condition為false,就永遠不會聲明並初始化 value。

&禁止重聲明

假設作用域中已經存在某個變量,此時再使用 let 關鍵字聲明一次就會錯誤:var count = 30;// 拋出語法錯誤let count = 40;

&const聲明

ECMAScript 6 標準還提供了 const 關鍵字。使用聲明的是常量,其值一旦被設定後不可更改。因此,每個通過 const 聲明的常量必須進行初始化// 有效的常量const maxNum = 30;// 語法錯誤:常量未初始化const minNum; const 與 let 聲明的都是塊級標識符,所以常量也只在當前代碼塊內有效,一旦執行到塊外會立即被銷毀。常量同樣也不會被提升至作用域頂部; 與 let 相似,在同一作用域中用 const 聲明已經存在的標識符也會導致語法錯誤;

&用 const 聲明對象

記住,const 聲明不允許修改綁定,但是允許修改值。這也就意味著用 const 聲明對象後,可以修改該對象的屬性值,但是不可以修改該對象的引用:const person = { name : ‘zhangsan‘}// 沒毛病person.name = ‘lisi‘;// 拋出語法錯誤person = { name : ‘wangwu‘}

#循環中的塊級作用域綁定

以下的代碼在JavaScript中很常見:for(var i=0;i<10;i++){ process(arr[i]);}console.log(i); // 打印10 由於 var 聲明得到提升,變量 i 的聲明提升至當前作用域頂部,在循環結束後依舊可以訪問它;如果換成 let 聲明變量:for(let i=0;i<10;i++){ process(arr[i]);}// i 在這裏不可訪問,拋出一個錯誤console.log(i); &循環中的函數 var 聲明讓開發者在循環中創建函數變得異常困難,對於新手會造成困惑,看下面的代碼:var funcs = [];for(var i=0;i<10;i++){ funcs.push(function(){ console.log(i); });}funcs.forEach(function(func){ func();}); 預期的結果是輸出數字 0 ~ 9,但是程序卻一直輸出 10; 這是因為循環裏的每次叠代同時共享著變量 i,循環內部創建的函數全部保留了對相同變量的引用。當循環結束時候,變量 i 的值為10,所以每次調用 console.log(i) 時就會輸出10; 為了解決上面的問題,可以在循環中使用 立即調用函數表達式(IIFE),以強制生成計數器變量的副本:var funcs = [];for(var i=0;i<10;i++){ funcs.push(function(value){ return function(){ console.log(value); } });}funcs.forEach(function(func){ func(); //依次輸出 0 ~ 9}); 在循環內部,IIFE 表達式為接受的每一個變量 i 都創建了一個副本並存儲為變量 value。這個變量的值就是相應叠代創建的函數所使用的值。 ECMAScript 6 中的 let 和 const 提供的塊級綁定就無需這麽麻煩了; &循環中的let聲明 let 聲明模仿上面的 IIFE 所做的一切來簡化循環過程。var funcs = [];for(let i=0;i<10;i++){ funcs.push(function(){ console.log(i); });}funcs.forEach(function(func){ func(); //依次輸出 0 ~ 9}); &循環中的const ES6 標準中沒有明確指明不允許在循環中使用 const 聲明,然而,針對不同類型的循環它會表現出不同的行為。 對於普通的 for 循環,使用 const 聲明計數器,那肯定是會拋出錯誤的:var funcs = [];// 循環一次之後,拋出錯誤for(const i=0;i<10;i++){ //...} 但是在 for-in 或者 for-of 循環中使用 const 聲明的行為與使用 let 是一致的:var funcs = [];var obj = { a:true, b:true, c:true};// 不會產生錯誤for(const key in obj){ funcs.push(function(){ console.log(key); })}funcs.forEach(function(func){ func(); // 依次輸出 a,b,c}) 之所以可以在 for-infor-of 中使用 const 聲明,是因為每次叠代不會修改已有的綁定,而是會創建一個新的綁定

#全局作用域綁定

let 和 const 與 var 的另外一個是它們在全局作用域中的行為; 當 var 被用於全局作用域時,它會創建一個新的全局變量作為全局對象(瀏覽器環境下全局對象為window)的屬性。這也就意味著 var 可能會在無意中覆蓋一個已經存在的全局變量// 在瀏覽器中var RegExp = ‘Hello!‘;console.log(window.RegExp); // 輸出 Hello!var ncz = ‘Hi!‘;console.log(window.ncz); 即使全局對象 RegExp 對象定義在 window上,也不能幸免於被 var 聲明覆蓋掉; 如果在全局作用域中使用 let 或 const,會在全局作用域下創建一個新的綁定,但該綁定不會添加為全局對象的屬性。換句話說,用 let 或 const 不會覆蓋全局變量,而只能遮蔽它;// 在瀏覽器中let RegExp = ‘Hello!‘;console.log(RegExp); // 輸出 Hello!console.log(window.RegExp === RegExp); // falseconst ncz = ‘Hi!‘;console.log(ncz); // 輸出 Hi!console.log("ncz" in window); // false let 聲明的 RegExp 創建了一個綁定並遮蔽了全局的 RegExp 變量,結果就是 window.RegExp 和 RegExp 不相同,但不會破壞全局作用域。 同樣,const 聲明的 ncz 創建了一個綁定但沒有創建為全局對象的屬性。 如果不想為全局對象創建屬性,則使用 let 或 const 要安全得多。 tips:在瀏覽器中跨 frame 或者跨 window 訪問,依然得使用 var在全局對象下定義變量。

#塊級綁定最佳實踐

當更多的開發者遷移到ES6後,一種做法日益普及:默認使用 const ,只有確實需要改變變量的值時使用 let。 因為大部分變量的值在初始化後不應再改變,而預料外的變量值的改變是很多 bug 的源頭。這一理念獲得了很多人的支持。

塊級作用域綁定