ES6躬行記(1)——let和const

  古語云:「紙上得來終覺淺,絕知此事要躬行」。的確,無論看了多少本書,若是本身不實踐,那麼就很難領會其中的精髓。本身研讀過許多ES6相關的書籍和資料,平時工做中也會用到,但在用到時常常須要上搜索引擎中查詢相關知識概念,而且對不少知識也僅僅是略知一二,沒有領會到其中的原理。爲此,開闢了《ES6躬行記》系列,將ES6相關的知識系統的記錄下來,以便本身翻閱,也但願能幫助到廣大網友。git

  在ES6以前的版本中,用於聲明變量的關鍵字只有var,而且沒有塊級做用域,只有函數做用域和全局做用域,但在ES6中已改變這種情況。ES6引入了let和const兩個關鍵字,它們既能夠用於聲明變量,還可以將變量綁定到當前所處的任意做用域中,換句話說,就是把變量的做用域封閉在所處的代碼塊(即花括號字符「{」和「}」之間的區域,例如if條件語句中的代碼)中,如此一來就造成了塊級做用域。let和const兩個關鍵字與var之間的不一樣,簡單的說有如下三點:編程

(1)不容許聲明提高。瀏覽器

(2)不容許重複聲明。異步

(3)不覆蓋全局變量。編程語言

  而在let與const之間也有不一樣之處,在下面的內容中會重點講解。模塊化

1、let

  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對象

2、const

  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;
};

3、循環中的let和const

  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);
}
相關文章
相關標籤/搜索