一、前言
二、let與var的區別
1.let 不能重復聲明,但var可以
var a = 1; var a = 2;//不會報錯 let b = 1; let b = 2;//報錯
2.let會產生塊級作用域,且只在自己的作用域內生效,但var不受限制
ES5 只有全局作用域和函數作用域,那什么是塊級作用域?當我們在{}中使用了let或者const時,{}的范圍就是一個塊級作用域,此時let或const只能在{}中訪問,像這樣:
{ let a = 1; var b = 2; } console.log(a);//報錯 console.log(a);//2
或者這樣:
if(true){ let a = 1; var b = 1; } console.log(a);//報錯 console.log(b);//1
//for循環用var var a = []; for (var i = 0; i < 5; i++) { a[i] = function () { console.log(i); }; } a[1]();//5 a[2]();//5 a[3]();//5 //for循環用let var b = []; for (let i = 0; i < 5; i++) { b[i] = function () { console.log(i); }; } b[1]();//1 b[2]();//2 b[3]();//3
在明白上面2個循環的區別之前,我們先來理一理知識點,我們要知道,for循環中設置循環變量的部位其實是一個父作用域,循環體內部是個子作用域。
在父子作用域中使用let聲明同一變量時,對於此變量而言,兩個作用域的同名變量其實是互不相關的兩個獨立變量:
{ let a = 1; { console.log(a);//報錯,暫時性死域,在let a前面使用a不合法 let a = 2; console.log(a);//2 } console.log(a);//1 }
即便父子作用域都使用了let,子作用域也能正常繼承自己非let的變量,父也能讀取子作用域非let的變量。
{ let a = 1; {
//這里使用了let,也是塊級作用域,但也能正常訪問父塊級作用域的變量a
let c = 1; var b = 2; console.log(a);//1 } console.log(a);//1 console.log(b);//2 }
所以下面這個循環輸出了三次echo,因為在子作用域中聲明的i跟父作用域中的i可以說是完全不同的兩個i;
for(let i = 0;i<3;i++){ let i = 'echo'; console.log(i); };//輸出三次echo
但如果你將let改為var,你會發現只輸出一次,因為沒有了塊級作用域,父子作用域共用了一個變量i,第一次循環后,子作用域的i被改為了echo,父作用域中i<3的判斷無法通過,所以只輸出了一次。
那么回頭再看看最初的兩個循環,為什么var聲明輸出了3個5,而let輸出了1,2,3呢,嘗試去理解,說到底還是塊級作用域搞的鬼。
3.let不存在變量提升
console.log(a);//undefined var a = 1; console.log(b);//報錯 let b = 1;
var a = 1; function f() { console.log(a);//undefined if (false) { var a = 2; } } f();
var a = 1; function f() { var a; console.log(a);//undefined if (false) { a = 2; } } f();
就問你,驚不驚喜?意不意外?

4.let存在暫時性死域
{ let a = 1; { console.log(a);//報錯 let a =2; } }
三、const聲明的特點
可以理解為,let的特性const都有,不能反復聲明,存在塊級作用域,不存在變量提升,也有暫時性死域的特點。
與let區別在於,const聲明的是一個常量,一旦聲明就無法修改,有個誤區需要特別注意,舉個例子:
const a = {}; a.b = 1; console.log(a);//{b:1}
上述代碼中我聲明了一個常量對象a,但是a對象修改了,並沒有報錯,這是為什么呢?從本質上理解,const聲明的變量並非變量的值不能修改,而是可以理解為變量的指向不能修改。
只是基本類型的數據,鍵值都在棧內存中,但對於復雜數據類型,變量指向的其實是堆內存中存放值所提供的一個地址。所以我們不能修改復雜數據類型的指向,這樣就會報錯:
const a = {}; a.b = 1; console.log(a);//{b:1} a = [];//這里直接修改了變量a的指向,不允許了。 console.log(a);//報錯
四、小總結
let與const的出現對於JS有什么影響或者說好處呢?
我們知道,在ES5環境中只有全局作用域與函數作用域,這也導致我們通過var聲明的全局變量直接與全局對象掛鈎,而瀏覽器環境全局對象是window,所以我們隨便聲明的全局對象都可以通過window訪問,很明顯,這樣的環境很容易造成混亂。
var a = 1; console.log(window.a)//1
而ES6帶來的let與const,可以說是宣告天下,從此我全局變量將逐步與頂層對象屬性脫離開來,還你一片極樂凈土。
當然,ES6中var與function聲明的全局變量還是會出現在頂層變量中,但至少現在提供了一種解決方式,JS語言也在成長的越來越好。
番外:
在記錄完let與var在for循環中的不同變現,我覺得我解釋的不夠合理,還是得單獨拿出來說下,即使let與var只是做聲明這件小事,我在拆分for循環步驟時,發現了一個很有趣的問題,所以深挖了一下。有興趣可以閱讀我的這篇博客: