古語云:「紙上得來終覺淺,絕知此事要躬行」。的確,無論看了多少本書,若是本身不實踐,那麼就很難領會其中的精髓。本身研讀過許多ES6相關的書籍和資料,平時工做中也會用到,但在用到時常常須要上搜索引擎中查詢相關知識概念,而且對不少知識也僅僅是略知一二,沒有領會到其中的原理。爲此,開闢了《ES6躬行記》系列,將ES6相關的知識系統的記錄下來,以便本身翻閱,也但願能幫助到廣大網友。git
在ES6以前的版本中,用於聲明變量的關鍵字只有var,而且沒有塊級做用域,只有函數做用域和全局做用域,但在ES6中已改變這種情況。ES6引入了let和const兩個關鍵字,它們既能夠用於聲明變量,還可以將變量綁定到當前所處的任意做用域中,換句話說,就是把變量的做用域封閉在所處的代碼塊(即花括號字符「{」和「}」之間的區域,例如if條件語句中的代碼)中,如此一來就造成了塊級做用域。let和const兩個關鍵字與var之間的不一樣,簡單的說有如下三點:編程
(1)不容許聲明提高。瀏覽器
(2)不容許重複聲明。異步
(3)不覆蓋全局變量。編程語言
而在let與const之間也有不一樣之處,在下面的內容中會重點講解。模塊化
let能夠理解爲var的升級版本,摒棄或糾正了var的一些會致使代碼混亂的特性,從而使得代碼的邏輯更清晰,可維護性更高。函數
1)提高搜索引擎
首先要介紹的是用let聲明的變量,其聲明語句不會再被提高。下面將兩個變量分別用var和let聲明,再輸出它們的結果,具體以下所示。編碼
console.log(outer); //undefined console.log(inner); //拋出未定義的引用錯誤 { console.log(outer); //undefined console.log(inner); //拋出未定義的引用錯誤 var outer = true; let inner = true; console.log(outer); //true console.log(inner); //true } console.log(outer); //true console.log(inner); //拋出未定義的引用錯誤
兩個變量都在代碼塊中執行賦值操做。第一個outer變量因爲是用var聲明的,所以它的聲明語句可以被提高,而在第一次和第二次輸出時,由於還沒被賦值,因此輸出的結果都爲undefined,在賦完值後,輸出的結果就都是true。第二個inner變量是用let聲明的,因爲聲明語句不能被提高到代碼塊的外部,所以它的做用域只限於代碼塊內,在代碼塊外就沒法訪問到它,而且在聲明它以前也沒法被訪問(由於聲明語句也不會被提高至做用域頂部),此時變量正處於臨時死區中。若是強行訪問,那麼就會拋出未定義的引用錯誤。spa
臨時死區(Temporal Dead Zone,簡稱TDZ)也叫暫時性死區,用let或const聲明的變量,在聲明以前都會被放到TDZ中,而在此時訪問這些變量就會觸發運行時錯誤。這種語法設計能促進工程師們在平時養成良好的編碼習慣,減小或杜絕一些因爲始料未及的緣由而產生的程序BUG。有一點要注意,TDZ並非由ECMAScript標準命名的,它是JavaScript社區的一種約定俗成的叫法。
2)重複聲明
接下來介紹let的第二個特性:不容許重複聲明。注意,這裏有一個前置條件,那就是隻有在相同做用域時,纔不容許同一個變量重複聲明。ES6引入的這個重複聲明的檢查機制,能夠避免在多人協做開發的時候,因多聲明一個或多個同名的變量,而影響別人代碼邏輯的狀況出現。下面的示例就分了兩個做用域分別說明,而且對比了var和let對待重複聲明的處理結果。
var duplicate; let repeat; var duplicate; //var聲明的變量可重複聲明 let duplicate; //拋出重複聲明的語法錯誤 let repeat; //拋出重複聲明的語法錯誤 { let repeat; //不一樣做用域,可正常聲明 }
3)全局做用域
最後介紹的是let在全局做用域中的特性。當用var在全局做用域中聲明變量的時候,該變量不但會成爲全局變量,並且還會成爲全局對象(例如瀏覽器中的window對象)的一個屬性。全局對象以及它的屬性能夠在任何位置被訪問,這樣就影響了函數的封裝,不利於代碼的模塊化,而且新聲明的全局變量有可能會覆蓋全局對象中已存在的屬性。上述是兩個比較有表明性的弊端,爲了解決這些問題,ES6規定用let可將全局變量和全局對象斷開聯繫。下面用兩組代碼分別演示斷開聯繫(第一組)和覆蓋已有屬性(第二組),注意,在第二組代碼中爲了方便對比,忽略了重複聲明的錯誤。
//第一組 var global = true; console.log(window.global); //true let whole = true; console.log(window.whole); //undefined //第二組 var Math = true; console.log(window.Math); //true let Math = true; console.log(window.Math); //Math對象
const不但擁有上面所述的let的三個特性,而且還能聲明一個常量。常量是指一個定義了初始值後固定不變的只讀變量。在過去,常量都是經過命名規範(例如用大寫字母、下畫線等字符)定義的,雖然實現起來很便捷,但因爲缺乏約束,所以這種常量很容易被修改。而在ES6引入了const關鍵字後,就能像其它編程語言那樣聲明常量了。有一點要注意,const與let不一樣,在聲明時必須初始化(即賦值),而且在設定後,其值沒法再更改,以下代碼所示。
const number; //拋出未初始化的語法錯誤 const digit = 10; digit = 20; //拋出賦值給常量的類型錯誤
此處要強調一點,const限制的實際上是變量與內存地址之間的綁定,也就是說,const讓變量沒法更改所對應的內存地址。若是是基本類型(例如布爾值、數字等)的變量,那麼對應的內存地址中保存的就是值;若是是引用類型(例如對象)的變量,那麼對應的內存地址中保存的是指向實際數據的一個指針。由此可知,當用const聲明的變量,其初始化的值是對象時,能夠修改對象中的屬性或方法,具體可參考下面的代碼。
const obj = {}; obj.name = "strick"; obj.age = function() { return 29; };
ES6規定了let聲明在循環內部的行爲,以for循環爲例,以下所示。
for (let i = 0; i < 3; i++) { setTimeout(function() { console.log(i); }, 0); }
在控制檯輸出的結果依次爲0、一、2,沒有出現循環中的異步回調問題。這是由於在每次循環的時候,都會從新建立一個叫作i的同名變量,並將其初始化爲計算後的值,而循環體內調用的i變量不會受其它同名變量的影響,因此可以在定時器的回調函數中正確顯示該變量的值。在ES5及以前的版本中,若是要解決異步回調問題,就須要像下面這樣藉助當即執行函數表達式(IIFE)才能獲得預期的效果。
for (var i = 0; i < 3; i++) { (function(n) { setTimeout(function() { console.log(n); }, 0); })(i); }
const聲明在循環內部的行爲與let聲明相似,不過,因爲其值沒法修改,所以在for循環中從新賦值(例如執行增量操做)將會拋出異常。但只要避開從新賦值,就能正常循環迭代,例如用for-in執行const聲明的循環,以下所示。
var author = { name: "strick", age: 29 }; for (const key in author) { console.log(key); }