这是我参与更文挑战的第28天,活动详情查看: 更文挑战
块作用域
上面我们说了,在es6之前,js中是没有块作用域的,在es6中,添加了let
关键字实现了对块级作用域的支持。那么什么是块级作用域呢,其实就是两个大括号包裹的作用域。而且在我们日常的代码中非常常见,比如if语句后跟的大括号,for循环后跟的大括号。那有的同学会说,这不是有块级作用域吗,那为什么又说没有块级作用域呢?我们又怎么区分有没有块级作用域呢?其实很简单,我们来看看代码就知道了。
if (true) {
var test = 'hello'
}
console.log(test) // hello
复制代码
看,我们在大括号外也访问到了大括号里面的变量,上面我们说了,在局部作用域(块级作用域也属于局部作用域)外面是访问不到作用域里面的变量的,所以这里的‘块作用域’其实并没有真正的形成作用域,只不过是徒有其表罢了,这样子的危害就是容易污染全局作用域,而且容易给我们造成一定程度上的误解。比如下面这样子。
var index = 5
for (var index = 0; index < 10; index++) {
/**/
}
console.log(index) // 10
复制代码
很明显,上面代码中for循环中的index污染了全局作用域中的index,如果我们不小心的话很容易造成意想不到的后果,当然我们也可以尽量小心的去给变量命名,细心的检查代码,或者使用try…catch(感兴趣可以去搜一下,或者看js高级程序设计)去实现块作用域,以便代码如我们想象般的运行,可那样就会花费更多的精力,好在es6推出了let
关键字,从代码层面支持了块作用域,减少了我们很多的工作量,来看看let
的效果
var index = 5
for (let index = 0; index < 10; index++) {
/**/
}
console.log(index) // 5
复制代码
看,代码完全按照我们想象那样执行,let
声明的变量支持块作用域,仅在块作用域内可访问,不会影响全局变量。
下面我们就来看看很经典的一道面试题
for (var index = 0; index < 6; index++) {
setTimeout(function(){
console.log(index)
})
}
复制代码
知道了块作用域再理解这道题就很简单了吧,因为这里用的是var
关键字,所以这里没有块作用域,也就是说这里的index其实是一个全局变量,然后每次对index进行++的操作其实都是操作的同一个变量——全局变量index,然后我们里面又用的是setTimeout
,一个异步函数,虽然我们这里没有设置定时时间,但它还是一个异步函数,需要等到for循环全部结束后才会运行,这时候index就已经是6了,所以会打印出来6个6。那如果我们把var
换成let
呢?
for (let index = 0; index < 6; index++) {
setTimeout(function(){
console.log(index)
})
}
复制代码
打印结果:0 1 2 3 4 5
完全符合我们的预期,这里我们使用的是let
,let
声明的变量支持块作用域,也就是仅在当前作用域内有效,所以这里我们循环中的每一个setTimeout
引用的index其实都是单独的变量,互不影响。