for循环中var的表现
for(var i= 0;i<5;i++){
setTimeout(()=>{ console.log(i) }, 1000)
}
输出结果:
5,5,5,5,5
为什么是5个5,而不是0,1,2,3,4呢?
var的表现不符预期的解释
一个解释是因为setTimeout是异步函数,异步函数不会堵塞for循环的执行,
所以当setTimeout开始执行之前,for循环已经结束了,变量i
已等于5
但setTImeout不足以解释输出异常的原因
因为for每循环一次都会创建一个新作用域,
前一个作用域的i
与后一个作用域的i
声明在不同的作用域内,两个i
是相互独立的。
因此正常理解,第一个作用域i
=0,第二个作用域i
=1,以此类推。各个i
都是互不影响,不同setTimeout读取的i
都不一样,输出结果应该为0,1,2,3,4
这里的异常是因为var把变量i
提升到for循环外部了,每一次新作用域声明的i
,都是对外部i
的更改
接下来我们证明下这两点:
- for循环每循环一次创建一个新的作用域
for(var i =0;i<5;i++){
let j=i
console.log(j)
}
输出结果:
0,1,2,3,4
我们利用let的特性来证明,因为let声明的变量一个作用域内只能声明一次,
如果上面的循环报错,则for的每一次循环都在同一作用域内。
然而上面for循环正常执行,即表明for每循环一次创建一个新的作用域。
- 第二var声明会把
i
提升到for循环外部
//在上面的for循环结束之后再打印i
console.log(i)
输出结果:
5
如果i
在作用域内部,外部就无法读取i
,因为外部i
还未声明。所以i
被提升到外部去了
我们可以用下面的代码来佐证下
for(let j = 0;j<5;j++){ console.log('') }
console.log(j)//报错,j未声明
为什么let表现符合预期
上面解释了for循环中var为什么表现不符合预期,于是我们就能理解为什么let会符合预期了
for(let i= 0;i<5;i++){
setTimeout(()=>{ console.log(i) }, 1000)
}
输出结果:
0,1,2,3,4
let声明变量不会变量提升,老老实实待在自己的作用域内搞事
for循环第一次,创建一个作用域,let i = 0, setTimeout读取到的i
为0
for循环第二次,再新建一个作用域,let i =1,setTimeout读取到的i
为1
…
结束