5分钟吃透 for 循环内使用setTimeout

82 阅读2分钟

「 涵盖的核心知识点 」

  1. 变量声明提升(var)
  2. 块级作用域(let、const)
  3. JS单线程
  4. 宏任务(EventLoop)
  5. 闭包

看一段代码,描述它做了什么?

for(var i = 0; i < 5; i++) {
    console.log(i);
}   

问题一:上面for循环执行顺序是什么?

  • var i = 0; -------- 表达式1;
  • i < 5; ------------ 表达式2;
  • {console.log(i);}-- 代码块3;
  • i++; -------------- 表达式4;

第一次执行:表达式1;表达式2;代码块3;表达式4;

第二次执行:表达式2;代码块3;表达式4;

第三次执行:表达式2;代码块3;表达式4;

...... 直到 (表达式2) 为假结束循环。

问题二:执行的时候做了什么?

//变量声明提升
var i;
for(i = 0; i < 5; i++) {
    console.log('内部i',i);//0 1 2 3 4
}
console.log('外层i',i);// 5

//外层打印5的原因?
//因为for循环是同步执行的,所以只有当for循环结束以后才会执行外层console.log('外层i',i),此时i是5。

再来看一段代码

var i;
for(i = 0; i < 5; i++) {
    setTimeout(() => {
        console.log(i);//5 5 5 5 5
    }, 10);
}
//打印5的原因?
//因为js是单线程的,一次只能执行一段代码,也就是同步执行,会发生阻塞。那么js引擎在解析js代码的时候遇到宏任务时会把它放入宏任务队列,等待同步任务执行完毕再去执行宏任务。(这里不考虑微任务和多个宏任务)

如何解决上面问题呢?

//方法一 块级作用域:
for(let i = 0; i < 5; i++) {
    setTimeout(() => {
        console.log(i);//5 5 5 5 5
    }, 10)
}
//方法二 闭包:
for(var i = 0; i < 5; i++) {
    (function(j){
        setTimeout(() => {
            console.log(j);//0 1 2 3 4
        }, 10)
    })(i);
}

详解块级作用域

当使用let、const时会产生块级作用域,什么是块级作用域?

{
    let a = 10;
    var b = 20;
}
//console.log(a);//报错 a is not defined
//console.log(b);// 20

//花括号包裹的这个环境就是块级作用域,块级作用域外面无法访问,那么for循环中let定义的i只在当前循环中有效,
// 那在下一次循环时是如何拿到上一次的i值呢?
//js引擎会帮我们记住上一次的值。

补充知识: 暂时性死区? 在let const声明变量xxx之前都属于暂时性死区。

面试for循环有的聊了... 随便记录一下。欢迎各位大佬补充修正...