ECMAScript是一種由Ecma國際(前身爲歐洲計算機制造商協會)以JavaScript爲基礎制定的一種腳本語言標準。目前,該標準基本上每一年發佈一次新的ES規範標準,目前最新的標準是ECMAScript 2018(ES9),因爲前端開發的應用場景日益複雜,自從費時六年之久ES6(ECMAScript 2015)的出現,增長了不少新的特性,讓JavaScript語言更加標準化和工程化。所以咱們有必要從新學習JavaScript,這樣才能適應前端突飛猛進的發展。javascript
從今天開始,小編將會介紹ES6及之後的相關內容,爲了便於理解和學習,每篇文章儘可能簡短。本篇文章小編將帶着你們一塊兒學習如何使用新的語法let聲明變量。前端
本篇文章閱讀時間預計10分鐘。java
ES6引入了let,用let聲明變量,解決了JavaScript沒有塊級做用域的問題(注:ES3的catch分句會產生塊做用域)。es6
有其它語言背景的好比JAVA,C#開發者來講,這個概念並不難以理解,反而ES6以前,JavaScript沒有塊級做用域,對於新手而言,使用var聲明變量,會讓JavaScript不易懂和難以調試,用很差,甚至會有內存泄露的可能性。爲何會這樣,主要是沒有清楚做用域的概念,接下來咱們首先了解下什麼是做用域。bash
做用域簡單的來講,就是一套尋找變量的規則,用於肯定在何處以及如何查找變量。說直白點:這些變量在哪裏?它們存儲在哪裏?編譯器如何找到它們?ES6代碼以前,只有全局做用域或函數做用域。微信
當一個塊或函數嵌套在另外一個函數時,就發生了做用域嵌套。如圖所示,就有三個嵌套做用域:函數
如何在嵌套做用域中尋找變量呢:引擎從當前做用域開始查找變量,若是找不到,就會向上一級繼續查找。當抵達最外層全局做用域時,不管找到仍是沒有找到,查找過程當中都會中止。學習
如何理解全局做用域和函數做用域呢,使用var聲明變量時,若是在函數外聲明,就是全局變量,任何函數均可以進行使用,這就是全局做用域查找。若是在函數內使用var聲明變量,就是函數做用域查找,只能在函數內部進行訪問,外部不能進行訪問,以下段代碼所示:ui
var a = 12; // 全局做用域都能訪問
function myFunction() {
console.log(a); // alerts 12
var b = 13;
if(true) {
var c = 14; // 函數內部能夠訪問
alert(b); // alerts 13
}
alert(c); // alerts 14
}
myFunction();
alert(b); // alerts undefined複製代碼
爲何b變量訪問不到?由於變量b是在函數內進行聲明的,所以函數執行完後,因爲垃圾數據回收機制的存在,引擎認爲函數執行完了,變量應該進行銷燬。this
若是你在函數內忘記寫了b標識前忘記寫了var,引擎就會自做聰明,在函數外全局做用域爲你自動聲明變量b,這樣在函數外就能訪問b變量了(全局做用域)。
所以使用var進行聲明時,若是一不當心,你就會聲明一個全局做用域的變量,更糟糕的狀況還有可能污染一個同名的變量,所以產生的BUG就很難查找。
如下這個例子會更加明顯,也是開發者常常會出現的問題,i變量會綁定到外部做用域(函數或全局做用域),污染整個外部做用域:
for(var i=0;i<10;i++){
console.log(i); //依次輸出1到9
}
console.log(i);//10複製代碼
幸虧es6引入了let,避免了有var聲明變量的一些問題,讓變量和函數不只能夠屬於所處的做用域,也能夠屬於某個代碼塊(一般是{...}內部),有一點須要強調,在塊級做用域定義的變量,塊級做用域外是沒法訪問的,以下段代碼所示:
let a = 12; // 全局做用域,能夠訪問
function myFunction() {
console.log(a); // alerts 12
let b = 13;
if(true) {
let c = 14; // this is NOT accessible throughout the function!
alert(b); // alerts 13
}
alert(c); // alerts undefined {}外,所以沒法訪問
}
myFunction();
alert(b); // alerts undefined {}外,所以沒法訪問複製代碼
在for循環體,使用var和let的區別更加明顯,一個是在全局做用域進行查找變量,一個是在塊級做用域查找變量,塊級做用域每一次執行都會產生一個做用域。首先在for循環裏,使用var聲明變量,以下段代碼所示:
for(var i=0;i<5;i++){
setTimeout(function() {
console.log(i);
}, 1000);
}
// 輸出 5 5 5 5 5複製代碼
因爲JavaScript是單線程,事件循環機制的存在(不太瞭解事件循環機制的,你們能夠查看《JavaScript基礎——你真的清楚JavaScript是什麼嗎?》),主線程執行for循環後,纔會執行SetTimeOut裏的函數,因爲使用var聲明的變量,做用域會綁定for循環的上一層做用域,因爲for循環執行完後,i的變量天然就等於5,所以setTimeOut在執行內部函數時,查找i變量的值,纔會輸出5。如圖所示變量尋找路徑:
將var替換let,將會輸出什麼結果,以下段代碼所示:
for(let i=0;i<5;i++){
setTimeout(function() {
console.log(i);
}, 1000);
}
// 輸出 0,1,2,3,4複製代碼
因爲塊級做用域的存在,每次循環,就會產生一個循環體塊級做用域,所以纔會達到預期的輸出。如圖所示變量尋找路徑:
對比項 |
let |
var |
---|---|---|
聲明變量 |
√ |
√ |
能夠被釋放 |
√ |
√ |
能夠被提高 |
|
√ |
重複定義檢查 |
√ |
|
可被用於塊狀做用域 |
√ |
|
用var在同一個做用域重複定義變量,後者將會覆蓋前者聲明的變量的值,以下段代碼所示:
var a = 0;
var a = 1;
alert(a); // alerts 1
function myFunction() {
var b = 2;
var b = 3;
alert(b); // alerts 3
}
myFunction();複製代碼
使用let在同一做用域下重複定義變量,將會產生SyntaxError的錯誤,以下段代碼所示:
let a = 0;
let a = 1; // SyntaxError
function myFunction() {
let b = 2;
let b = 3; // SyntaxError
if(true) {
let c = 4;
let c = 5; // SyntaxError
}
}
myFunction();複製代碼
若是你在嵌套做用域裏進行從新定義變量,雖然變量名相同,可是不是同一變量,以下段代碼所示:
var a = 1;
let b = 2;
function myFunction() {
var a = 3; // different variable
let b = 4; // different variable
if(true) {
var a = 5; // overwritten
let b = 6; // different variable
console.log(a); // 5
console.log(b); // 6
}
console.log(a); // 5
console.log(b); // 4
}
myFunction();
console.log(a);
console.log(b);複製代碼
初學JavaScript的同窗,直覺上會認爲編譯器會由上到下一行行的執行,其實並不正確,函數聲明和變量聲明都會被提高(使用var聲明變量,let聲明變量將不會被提高)。函數首先會被提高,而後纔是變量提高。
首先咱們看下段函數提高的代碼:
bookName("ES8 Concepts");
function bookName(name) {
console.log("I'm reading " + name);//I'm reading ES8 Concepts
}複製代碼
正常輸出,因爲函數會提高至執行語句前。在來看如下代碼,使用變量的方式聲明函數:
bookName("ES8 Concepts"); //TypeError: bookName is not a function
var bookName = function(name) {
console.log("I'm reading " + name);
}複製代碼
爲何會這樣呢,JavaScript引擎只會先提高函數,在提高變量聲明,引擎將會對上述代碼這樣調整,代碼以下:
var bookName; // 變量聲明提高至最上面
bookName("ES8 Concepts"); // bookName is not function
// because bookName is undefined
bookName = function(name) { // 變量賦值不會被提高
console.log("I'm reading " + name);
}複製代碼
若是使用let替換var聲明函數呢?將會有什麼提示輸出呢?以下段代碼所示:
bookName("ES8 Concepts"); // ReferenceError: bookName is not defined
let bookName = function(name) {
console.log("I'm reading " + name);
}複製代碼
從中能夠看出,使用let聲明的變量將不會產生變量聲明提高。這樣的好處就是,讓咱們更好的按照由上到下的常規方式書寫代碼,儘可能避免提高問題產生的難以查找的問題。
今天的文章就到這裏,從中咱們能夠看出let能夠說是var的進化版,爲了不產生奇奇怪怪的問題,讓咱們能按照大多數高級語言書寫代碼的思惟方式,在絕大部分狀況下,咱們應該使用let聲明變量,讓咱們的代碼更加易於維護和使用。
本文部份內容參:《你不知道的JavaScript》
更多精彩內容,請微信關注」前端達人」公衆號!