原文:http://www.javashuo.com/article/p-bycakzht-ga.htmlhtml
http://www.javashuo.com/article/p-kdrbbfvg-gn.html-----let和const----你所不知道的JavaScript系列(2)git
在ES6中多了兩個變量定義的操做符——let和const,在如今項目中,ES6已是不可獲缺,我打算在掘金上整理一套ES6的系列,會收集經常使用的知識點,喜歡的能夠點個喜歡,關注,或者能夠去github點個stares6
你們都知道js是沒有塊級做用域的,咱們先了解一下塊級做用域。github
任何一對花括號中的語句集都屬於一個塊,在這之中定義的全部變量在代碼塊外都是不可見的面試
瞭解定義以後,咱們👀一個用爛了的例子:segmentfault
for(var i = 0; i < 10; i++) { console.log(1); } console.log(i);複製代碼
上面這個例子,最外面會輸出10。顯而易見,沒有塊級做用域。安全
關於這一點咱們能夠看道面試題就能明白。函數
var func = []; for(var i = 0; i < 10; i++) { func.push(function(){ console.log(i) }); } func.forEach((func) => { func(); }) //10個10複製代碼
爲何會產生這樣的事情呢?由於在循環內部這些i都是用同一個詞法做用域的,換言之,這10個i用的都是最後的輸出的i,最後的i也就等於10。
而當即執行函數就不同,他用函數做用域代替塊級做用域,強制在循環內部建立副本,以便輸出1,2,3,4...post
var func = []; for(var i = 0; i < 10; i++) { func.push((function(value){ return function() { console.log(value) } })(i)); } func.forEach((func) => { func(); }) //會輸出1到9複製代碼
對當即執行函數有興趣的好能夠看看這麼幾篇博文,我在這裏就不用大篇幅贅述,咱們簡單過一下下面幾種方法,而後去將咱們今天的主角們。
推薦博文
推薦博文ui
try-catch這個建立塊級做用域在紅皮書中有提到過,我沒用過,以爲這個知識點了解就能夠,也不經常使用。
try { throw 'myException'; } catch (e) { console.log(e); } console.log(e); //第一個輸出myException,第二個輸出e is not defined複製代碼
在ES6中提出了let和const,咱們能夠看一下下面這幾個例子,在每次循環中,let會建立一個詞法做用域,並與以前迭代中同名變量的值將其初始化。
for(let i = 0; i < 10; i++) { console.log(1); } console.log(i); //報錯i is not defined複製代碼
const func = []; for(let i = 0; i < 10; i++) { func.push(function(){ console.log(i) }); } func.forEach((func) => { func(); }) //會輸出0到9複製代碼
這個特性一樣適用於for in
const funcs = [], obj = { a: 'lai', b: 'hua', c: 'min' }; for (let key in obj) { funcs.push(() => { console.log(key) }) } funcs.forEach((func) => { func() }); //輸出的是a b c複製代碼
在一個做用域中,已經用var、let、const聲明過某標識符以後,不能在用let、const聲明變量,否則會拋出錯誤
var a = 0; let a = 10; // 報錯複製代碼
可是在做用域中嵌套一個做用域就不會,看下面這個例子
var a = 0; if (true) { let a = 10; } // 不會報錯複製代碼
const效果也是一致的,不過const用於定義常量,const還有如下特性
當你用const聲明變量,不初始化的話,就會發生報錯
const a; // 報錯複製代碼
而const的本質是聲明的,不容許修改綁定,可是容許修改值,因此大多數場景,咱們都用const來聲明對象,那樣對象的指針不會改變,相對來講安全,看一下下面的例子
const person = { name = 'laihuamin' } person.name = 'lai'; //到這裏不會發生報錯,只會改變值 person = {}; //這裏改變了對象的指針,因此會發生報錯複製代碼
而const不止能用於對象指針綁定,還能運用在for in的迭代中,由於每次迭代不會修改已有的綁定,而是會建立新的綁定。看下面的例子
const funcs = [], obj = { a: 'lai', b: 'hua', c: 'min' }; for (const key in obj) { funcs.push(() => { console.log(key) }) } funcs.forEach((func) => { func() }); //輸出a b c複製代碼
可是在循環中就不能用,循環會修改已有的綁定,而const定義的常量時不能修改綁定的,因此會報錯。
注:複合類型const變量保存的是引用。由於複合類型的常量不指向數據,而是指向數據(heap)所在的地址(stack),因此經過 const 聲明的複合類型只能保證其地址引用不變,但不能保證其數據不變。
對於ES5的變量提高有一個經典的考題。以下:
var a = 10; (function () { console.log(a); var a = 1; })(); // 這個會輸出undefined複製代碼
其實這個很好理解,js做用域連是從內向外尋找變量的,那麼函數的做用域中有a這個變量,因爲var會發生變量提高,就至關於下面這個過程
var a; console.log(a); a = 1;複製代碼
因此,這個a變量就是undefined。而let和const就不同,把var換成let或者const都會報錯。
咱們先來看例子,再來根據例子解析:
console.log(a); let a = 10; //Uncaught ReferenceError: a is not defined複製代碼
let和const定義的變量是存在暫時性死區的,而var沒有,咱們來了解一下兩個操做符的工做原理:
對於var而言,當進入var變量的做用域時,會當即爲他建立存儲空間,並對它進行初始化,賦值爲undefined,當函數加載到變量聲明語句時,會根據語句對變量賦值。
而let和const卻不同,當進入let變量的做用域時,會當即給他建立存儲空間,可是不會對他進行初始化,因此會拋出如上錯誤。
而對於typeof操做符來講,結果是一致的,同樣會報錯:
console.log(typeof a); let a = 10; //Uncaught SyntaxError: Identifier 'a' has already been declared複製代碼
因此最佳實踐是把聲明的變量所有提到做用域的開頭,這樣既方便管理,又能避免沒必要要的麻煩
var聲明全局變量的時候,當使用關鍵詞,那麼就會覆蓋掉window對象上本來擁有的屬性,咱們看一下下面這個例子:
var RegExp = 'lai'; console.log(window.RegExp); var a = 'hua'; console.log(window.a); var Array = 'min'; console.log(window.Array); var b = new Array(); //lai //hua //min //Uncaught TypeError: Array is not a constructor複製代碼
而換成let和const的時候就不會發生這樣的事情,咱們用一樣的例子來看一看:
let RegExp = 'lai'; console.log(window.RegExp); let a = 'hua'; console.log(window.a); let Array = 'min'; console.log(window.Array); let b = new window.Array(); console.log(b); //ƒ RegExp() { [native code] } //undefined //ƒ Array() { [native code] } //[]複製代碼
結果和上面同樣,咱們更能夠進一步認證
let RegExp = 'lai'; console.log(RegExp === window.RegExp); var Array = 'hua'; console.log(Array === window.Array); //會輸出 false 和 true複製代碼
根據以上講的,最佳實踐應該是,能用const定義對象的,不要用let,能用let定義變量的,不要用var。至於他的不少特性,瞭解了能更好的幫助你運用。若是以爲筆者寫的能夠的點一個喜歡,以後還會持續更新其餘板塊,但願能給筆者的github點個star,謝謝支持