ES6之前,JS都只用var聲明變量。ES6不僅增加了let和const兩個關鍵字,而且還讓這兩個關鍵字壓倒性的超越var成為首選。
1.var
使用var聲明變量,變量會被自動添加到最近的上下文(作用域)。
如在函數中,最近的上下文就是函數的局部上下文。如果變量未經聲明就被初始化了,那么它就會自動被添加到全局上下文:
function add(a,b){ var sum = a + b return sum } let res = add(1,2) // 3 console.log(sum); // 報錯undefined
說明:
- 函數add()定義了一個局部變量,保存加法的操作。這個值作為函數的值返回,由於使用了var關鍵字,這個sum變量的作用域在函數內,所以在函數外是無法訪問的。
如果省略add()函數內的var關鍵字,sun就可以訪問了:
function add(a,b){ sum = a + b return sum } let res = add(1,2) // 3 console.log(sum); // 3
說明:
- sum並沒有使用var聲明。在調用add()之后,sum被添加到了全局上下文,在函數退出后依然存在,因此函數外可以訪問
注意:
- 未經聲明而初始化變量會導致JS中的很多問題,因此初始化變量之前一定要先聲明變量
var聲明會被拿到函數或全局作用域的頂部——變量提升
就類似於:
var a = 1 // 相當於 var a a = 1
如在一個作用域內,一個變量的調用在變量的聲明之前的話,那么這個變量拿到的就是undefined:
console.log(a) // undefined var a = 1 // 相當於: var a console.log(a) a = 1
2.let
let的作用域是塊級的。塊級作用域由最近的一對包含花括號{}界定的。即:if、while、function、單獨的塊也是let聲明變量的作用域
在一個if、while中使用var 聲明的變量,在這個循環外面還是可以訪問到的。let就是解決了這個問題。在塊級作用域內使用let聲明的變量是無法在循環外訪問的。
一個非常經典的筆試題:
for(var i = 0 ;i <= 5; i++){ setTimeout(()=>{ console.log(i) },0) } // 6 6 6 6 6 6 for(let i=0;i<=5;i++){ setTimeout(()=>{ console.log(i) },0) } // 0 1 2 3 4 5
說明:
- 使用var聲明的變量,加上塊級作用域,每一次i++都是在同一個全局的i上面進行增加的。所以在調用棧中的主程序執行結束再逐個執行打印i的操作都是打印的循環結束以后的i(6)
- 使用let聲明的變量,塊級作用域(循環語句)以外是無法訪問的,每次的定時器執行的打印操作打印的都是當前循環的i的值
- 這里說的不太透徹,配合let聲明的變量沒有變量提升這個特點,可以理解為let聲明的for循環內是一個閉包,外界無法訪問。
let聲明的另一個特點:同一個變量不能用let聲明兩次
3.const
使用 const 聲明的變量必須同時初始化為某個值。一經聲明,在其生命周期的任何時候都不能再重新賦予新值。
注意:開發實踐表明,如果開發流程並不會因此而受到很大影響,就應該盡可能多的使用const聲明。