本文旨在,形成自己对在for循环中使用到var与let的区别
的理解
一、var
for(var i = 0; i < 3; i++){
setTimeout(function(){
console.log(i);
})
}
知识点:
-
var
声明提升- 使用
var
关键字声明的变量会自动提升到函数作用域顶部,为局部变量 - 若
var
声明不是包含在函数内,则提升到script
标签下方,成为全局变量
- 使用
-
for循环有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域
-
以下为
全局变量i
1.故原for循环可以分解为
var i = undefined //全局变量
i = 0
i < 3
{
setTimeout(() => {
console.log(i);
});
}
i++
// i == 1
i < 3
{
setTimeout(() => {
console.log(i);
});
}
i++
// i == 2
i < 3
{
setTimeout(() => {
console.log(i);
});
}
i++
// i == 3
知识点:
setTimeout
是异步执行,每一次for循环的时候,setTimeout
都执行一次,但是里面的回调函数
没有被执行,而是被放到了任务队列里,等待执行。只有主线上的任务执行完,才会执行任务队列里的任务。setTimeout
延时参数:没写,则默认延时0;这个延迟时间始终是相对主线执行完毕的那个时间算的 ,并且多个setTimeout执行的先后顺序也是由这个延迟时间决定的
2.进一步解析:
- 简化结果如下
//主线
var i = undefined
i = 0
i = 1
i = 2
i = 3
//任务队列,延时都为0
{
//setTimeout(
() => {
console.log(i);
}
//);
}
{
//setTimeout(
() => {
console.log(i);
}
//);
}
{
//setTimeout(
() => {
console.log(i);
}
//);
}
执行
- 从上往下执行
setTimeout
的回调函数
:延时都为0,同时执行;执行时找不到变量i
,到上一级作用域找,找到全局变量i
,值为3
3.原for循环执行结果如下
二、let
for(let j = 0; j < 3; j++){
setTimeout(function(){
console.log('j',j);
})
}
知识点:
- let 声明的范围是
块作用域
,即距离let
最近的外层{}
个人理解
:由于var
命令的变量提升机制,var
命令实际只会执行一次。而let
命令不存在变量提升,所以每次循环都会执行一次,声明一个新变量
(但初始化的值不一样),JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。for 的每次循环都是不同的块级作用域
,let 声明的变量是块级作用域的,所以也不存在重复声明的问题。《JavaScript高级程序设计4》
:使用 let 声明迭代变量时,JavaScript 引擎在后台会为每个迭代循环声明一个新的迭代变量,每个 setTimeout 引用的都是不同的变量实例
1.可以分解为
{
{//块作用域 1
let j = 0
j < 3
{
setTimeout(function(){
console.log(j);
}
j++
}
{//块作用域 2
let j = 1
j < 3
{
setTimeout(function(){
console.log(j);
}
j++
}
{//块作用域 3
let j = 2
j < 3
{
setTimeout(function(){
console.log(j);
}
j++
}
{//块作用域 4
let j = 3
}
}
知识点:
《JavaScript高级程序设计4》
:JavaScript 引擎会记录
用于变量声明的标识符及其所在的块作用域- 同样的:
setTimeout
是异步执行,被放到了任务队列里,等其余代码执行完毕,再执行里面的函数。 setTimeout
执行时找不到变量j
,到上一级块作用域 ( let声明的作用域 ) 找,找到变量j
- 执行结果如下:
三、总结
- 此处 var 定义的 i 是贯穿整个作用域的,而不是给每一个定时器分配一个 i ,定时器是异步,一定是在for运行完了以后,定时器内的回调函数才开始执行,此时 i 已经变成3了。
- 此处 let 定义的 j 具有块级作用域,给每一个定时器都分配了一个 j,for运行完了执行回调的时候,也就找到了对应的 j 。
- 将
setTimeout
换为其他的异步操作,也同理【如点击事件
的回调,当触发点击
的时候,for肯定已经运行完了】
四、查阅
- 找了很多文章来看,最后形成自己的理解,
如有错误,请不吝指正
- 《JavaScript高级程序设计(第四版)》
- 链接: 作用域
- 链接: 彻底理解setTimeout()
- 链接: 一点关于for循环和let、var声明的思考