当前位置: 首页 > news >正文

JS For循环中嵌套setTimeout()方法的理解

关于JavaScript中For循环中嵌套setTimeout()方法的理解,这种题常出现于面试中!!!

思考:

先上代码,我们可以看一遍,思考一下答案,对比两种方法的区别。

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 1)
}

for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 1)
}

答案: 3 ,3 ,3 和 0 ,1 ,2

解析:

我们通过在每一个阶段加入 console.log(i) 来更深层次的理解 js 的内部机制

       	console.log(i)                          // 输出顺序一 undefined
        for (var i = 0; i < 3; i++) {
            console.log(i);                     // 输出顺序二 0,1,2
            setTimeout(() => console.log(i), 1);    // 输出顺序六 3,3,3
        }
        
        console.log(i)                          // 输出顺序三 3

        for (let i = 0; i < 3; i++) {
            console.log(i);                     // 输出顺序四 0,1,2
            setTimeout(() => console.log(i), 1);    // 输出顺序七 0,1,2
        }
        console.log(i)                          // 输出顺序五 3

我们根据执行顺序分析一下每一个 console.log()内部发生了什么:

  1. 执行顺序一:变量提升问题
    在第一个 for 循环中 var 定义了 i ,提升至全局,此时 全局上下文GO中 { i: undefined }
  2. 执行顺序二:for循环机制
    每循环一次打印一次,同时修改了 GO 中 i 的值,注意 i++,先运算再赋值,跳出循环时 i = 3
  3. 执行顺序三:
    此时跳过 setTimeou t执行,输出全局中 i 的值, { i: 3 }
  4. 执行顺序四:let块级作用域
    此处因为是 let 声明 i ,let 在 {} 中形成了块级作用域,i 被重新声明, for循环一次打印一次 i 的值
  5. 执行顺序五:
    打印全局中 i 的值,这里和执行顺序三是一致的,也跳过了 setTimeou t执行
  6. 执行顺序六:setTimeout 异步执行(全局作用域)
    setTimeout是一个异步宏任务,他的回调函数会在遍历结束才执行,此时全局中 i=3,所以会输出3次3
  7. 执行顺序七:setTimeout 异步执行(块级作用域)
    在每次的遍历过程中,i 都有一个新值,并且每个值都在循环内的块级作用域中

引发:

  • 两种方式的区别:
    ①:因为 let 的作用域是块作用域,所以每次 JS 检测到 setTimeOut 把 setTimeOut 放到队列的同时,let 定义的i的值也会跟随 setTimeOut 进去队列。所以每次循环后队列里的 setTimeOu t里的i的值是不一样的。
    ②:而 var 定义的 i 是无法进入 setTimeOut 的。i 只能在运行到 setTimeOut 时才会向外层环境申请 i 的值,而这个时候 i 的值已经变成 3 了。

  • 修改第一种var定义的循环方式:

  1. 方式一:
    对第一种for循环问题的解决:因为 setTimeOut() 是异步执行,所以我们让他立即执行就可以了,使用立即执行函数。形成闭包,使得内部函数能够访问外部函数的变量。
    console.log(i);                     // undefined                     
    for (var i = 0; i < 3; i++) {
         console.log(i);                 // 同步,0,1,2
         (function (i) {
             console.log(i);             // 同步,0,1,2  
             setTimeout(function () {
                 console.log(i);         // 最后执行,0,1,2
             }, 1000);
         })(i);  
     }
    console.log(i)                      // 3
  1. 方式二:
    除此之外,我们还可以用到 setTimeout 第三个参数的特点实现效果。将 i 作为第三个参数传入,用 j来进行接收,这样 setTimeout 每次循环时访问的 i 值就会随之变化。
    	console.log(i);                        // undefined
        for (var i = 0; i < 3; i++) {
            console.log(i);                    // 0,1,2
            setTimeout( function timer(j) {
                console.log(j);                // 最后执行 0,1,2
            }, 1000, i);
        }
        console.log(i);                        // 3

相关文章:

  • ubuntu下zabbix服务器监控工具部署
  • 前端性能优化-图片
  • MapString, String循环遍历的方法
  • 快速掌握js中闭包的理解与应用(面试中如何回答闭包)
  • inkspace 0.92 安装 总结
  • js中原型,原型链的理解
  • CSS水平、垂直居中问题.md
  • Vue3写法总结
  • [python] os.path说明
  • vue-cli3+ 打包至非根目录静态资源(图片,字体文件)路径加载错误,导致不显示,丢失
  • 讲座:Influence maximization on big social graph
  • Vue3中组件的挂载及调用
  • HDFS 核心原理
  • 正则表达式学习1
  • 第一章:TypeScript
  • [deviceone开发]-do_Webview的基本示例
  • CSS进阶篇--用CSS开启硬件加速来提高网站性能
  • express.js的介绍及使用
  • fetch 从初识到应用
  • Git 使用集
  • java中的hashCode
  • JSDuck 与 AngularJS 融合技巧
  • LeetCode18.四数之和 JavaScript
  • leetcode-27. Remove Element
  • Mac 鼠须管 Rime 输入法 安装五笔输入法 教程
  • MySQL Access denied for user 'root'@'localhost' 解决方法
  • Rancher如何对接Ceph-RBD块存储
  • react-core-image-upload 一款轻量级图片上传裁剪插件
  • Solarized Scheme
  • 创建一种深思熟虑的文化
  • 前端每日实战:70# 视频演示如何用纯 CSS 创作一只徘徊的果冻怪兽
  • 如何设计一个比特币钱包服务
  • 微信小程序实战练习(仿五洲到家微信版)
  • 温故知新之javascript面向对象
  • 走向全栈之MongoDB的使用
  • Prometheus VS InfluxDB
  • #13 yum、编译安装与sed命令的使用
  • $refs 、$nextTic、动态组件、name的使用
  • ()、[]、{}、(())、[[]]命令替换
  • (4)事件处理——(2)在页面加载的时候执行任务(Performing tasks on page load)...
  • (C语言)二分查找 超详细
  • (附源码)spring boot智能服药提醒app 毕业设计 102151
  • (附源码)ssm基于jsp高校选课系统 毕业设计 291627
  • (附源码)小程序儿童艺术培训机构教育管理小程序 毕业设计 201740
  • (五)MySQL的备份及恢复
  • .mkp勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .net core 6 redis操作类
  • .NET 命令行参数包含应用程序路径吗?
  • .net 重复调用webservice_Java RMI 远程调用详解,优劣势说明
  • .NET 自定义中间件 判断是否存在 AllowAnonymousAttribute 特性 来判断是否需要身份验证
  • .net反编译工具
  • .NET分布式缓存Memcached从入门到实战
  • .NET企业级应用架构设计系列之技术选型
  • .one4-V-XXXXXXXX勒索病毒数据怎么处理|数据解密恢复
  • @Autowired多个相同类型bean装配问题