• 解决 for 循环中使用 var 定义函数的问题(解决循环中异步问题)


    结论先行:

    因为 setTimeout 为宏任务,根据 JS 的事件循环机制,当主线程同步任务执行完成之后才会去执行宏任务。这时候 for 循环已经全部执行完毕,i 就是最后的值了。

    解决办法:

    ① 使用立即执行函数将 i 传入函数内部,这个时候值就被固定在了参数 j 上不会改变;当下次执行 timer 这个闭包的时候,就可以使用外部函数的变量 j , 从而达到目的。 

    ② 使用 setTimeout 的第三个参数这个参数会被当成 timer 函数的参数传入。原理类似于方法①

    ③ 使用 let 定义 i 【推荐】。因为 let 可以定义块级作用域,所以不存在变量覆盖的问题。

     

    具体分析:

    1、问题 

    首先,因为 setTimeout 是个异步函数,所以会先把 for 循环全部执行完毕,这时候 i 就是 6了,所以会输出 5 个 6

    因为 setTimeout 为宏任务,由于JS 中单线程 eventLoop 机制,在主线程同步任务执行完后才去执行宏任务,因此循环结束后 setTimeout 中的回调才依次执行。

    而且, setTimeout 函数也是一种闭包,往上找它的父级作用域链就是 window,变量 i 为 window 上的全局变量,开始执行 setTimeout 之前变量 i 已经就是 6了,因此最后输出 5 个 6

    1. for(var i = 1; i <= 5; i++) {
    2. setTimeout(function timer() {
    3. console.log(i) // 打印6 6 6 6 6
    4. }, i * 1000)
    5. }

    2、解决办法

    ①  利用 IIFE (立即执行函数)

    使用立即执行函数将 i 传入函数内部,这个时候值就被固定在了参数 j 上不会改变;当下次执行 timer 这个闭包的时候,就可以使用外部函数的变量 j , 从而达到目的。 

    1. for(var i = 1; i <= 5; i++) {
    2. (function(j) {
    3. setTimeout(function timer() {
    4. console.log(j) // 打印1 2 3 4 5
    5. }, j * 1000)
    6. })(i)
    7. }

    ②  使用 setTimeout 的第三个参数

    setTimeout 作为经常使用的定时器,它是存在第三个参数的。我们经常使用前两个,一个是回调函数,另外一个是定时时间,setTimeout 从第三个入参位置开始往后,是可以传入无数个参数的。

    这些参数会作为回调函数的附加参数存在。

    这个参数会被当成 timer 函数的参数传入

    1. for(var i = 1; i <= 5; i++) {
    2. setTimeout(function timer(j) {
    3. console.log(j)
    4. }, i * 1000, i)
    5. }

    ③  使用 let 定义 i 【推荐】

    let 可以定义块级作用域,因为在异步的时候不存在变量覆盖的问题。

    ES6 中新增的 let 定义变量的方式,使得 ES6 之后 JS 发生革命性的变化,让 JS 有了块级作用域,代码的作用域以块级为单位进行执行。

    可以看到,通过 let 定义变量的方式,重新定义 i 变量,则可以用最少的改动成本,解决该问题。 

    1. for(let i = 1; i <= 5; i++) {
    2. setTimeout(function timer() {
    3. console.log(i)
    4. }, i * 1000)
    5. }

  • 相关阅读:
    项目实施集成方案
    Vue2.0开发之——Vue基础用法-侦听器(27)
    第21章 Spring事务管理之扩展篇(三)
    PyCharm使用心得体会1
    SpringBoot——自定义start
    ssm+vue+elementUI 高校普法系统-#毕业设计
    JDBC中execute、executeQuery和executeUpdate的区别
    Java语言开发的AI智慧导诊系统源码springboot+redis 3D互联网智导诊系统源码
    【移动端网页特效】01-触屏事件和常用对象列表
    VUE的列表渲染
  • 原文地址:https://blog.csdn.net/qq_38290251/article/details/133710020