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声明。