我们先来简单了解一下setTimeout延时器的运行机制。setTimeout会先将回调函数放到等待队列中,等待区域内其他主程序执行完毕后,按时间顺序先进先出执行回调函数。本质上是作用域的问题。

  因此若是这样将不会得到想要的结果输出1.2.3.4.5,而会连续输出5个6。

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

  这是因为setTimeout是异步执行,每一次for循环的时候,setTimeout都执行一次,但是里面的函数没有被执行,而是被放到了任务队列里,等待执行。只有主线上的任务执行完,才会执行任务队列里的任务。也就是说它会等到for循环全部运行完毕后,才会执行fun函数,但是当for循环结束后此时i的值已经变成了6,因此虽然定时器跑了5秒,控制台上的内容依然是6。

  (注意:for循环从开始到结束的过程,需要维持几微秒或几毫秒,当定时器跑完一秒之后for循环早已经做完了。)

  我们来看另一种情况:

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

  由setTimeout的运行机制可以知道,首先会运行外部的所有主程序,虽然for循环内形成了闭包,但是fun并没有发现一个实参所以跟第一个例子并无实际差别,仍然是连续输出5个6。

解决方案1:闭包

  使用闭包是很经典的一种做法:

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

  我们可以发现跟预期结果一致,依次输出1到5,因是因为实际参数跟定时器内部的i有强依赖。

  通过闭包,将i的变量驻留在内存中,当输出j时,引用的是外部函数的变量值i,i的值是根据循环来的,执行setTimeout时已经确定了里面的的输出了。

解决方案2:拆分结构

  我们还可以将setTimeout的定义和调用分别放到不同部分:

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

  控制台上输出依然是依次输出1到5。

解决方案3:let

  这里再来说一说使用es6的let来解决此问题:

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

  这个例子与第一个相比,只是把var更改成了let,可是控制台的结果却是依次输出1到5。

  因为for循环头部的let不仅将i绑定到for循环中,事实上它将其重新绑定到循环体的每一次迭代中,确保上一次迭代结束的值重新被赋值。setTimeout里面的function()属于一个新的域,通过var定义的变量是无法传入到这个函数执行域中的,通过使用let来声明块变量能作用于这个块,所以function就能使用i这个变量了;这个匿名函数的参数作用域和for参数的作用域不一样,是利用了这一点来完成的。这个匿名函数的作用域有点类似类的属性,是可以被内层方法使用的。

解决方案4:setTimeout第三个参数

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

  由于每次传入的参数是从for循环里面取到的值,所以会依次输出1到5。关于setTimeout第三个参数,下一篇会详细讲到,这里大家了解下就好。

关于for循环中使用setTimeout的四种解决方案的更多相关文章

  1. CSS布局中浮动问题的四种解决方案

    一.起因: 子盒子设置浮动之后效果: 由此可见,蓝色的盒子设置浮动之后,因为脱离了标准文档流,它撑不起父盒子的高度,导致父盒子高度塌陷.如果网页中出现了这种问题,会导致我们整个网页的布局紊乱 二.解决 ...

  2. for循环中嵌套setTimeout,执行顺序和结果该如何理解?

    这两天在捣鼓作用域的问题,有的时候知识这个东西真的有点像是牵一发而动全身的感觉.在理解作用域的时候,又看到了一道经典的面试题和例子题. 那就是在for循环中嵌套setTimeout延时,想想之前面试的 ...

  3. JS中For循环中嵌套setTimeout()方法的执行顺序

    在For循环中执行setTimeOut()方法的代码,执行顺序是怎样的呢? 代码如下 function time() { for(var i= 0;i<5;i++){ setTimeout(fu ...

  4. JS去除数组中重复值的四种方法

    JS去除数组中重复值的四种方法 1 /// <summary>            o[this[i]] = "";  }      }       newArr.p ...

  5. mysql中模糊查询的四种用法介绍

    下面介绍mysql中模糊查询的四种用法: 1,%:表示任意0个或多个字符.可匹配任意类型和长度的字符,有些情况下若是中文,请使用两个百分号(%%)表示. 比如 SELECT * FROM [user] ...

  6. JAVA中集合输出的四种方式

    在JAVA中Collection输出有四种方式,分别如下: 一) Iterator输出. 该方式适用于Collection的所有子类. public class Hello { public stat ...

  7. javaSE中JDK提供的四种线程池

    对javaSE中JDK提供的四种线程池稍作整理   一.Executor   package java.util.concurrent; /** * @since 1.5 * @author Doug ...

  8. PHP从数组中删除元素的四种方法实例

    PHP从数组中删除元素的四种方法实例 一.总结 一句话总结:unset(),array_splice(),array_diff(),array_diff_key() 二.PHP从数组中删除元素的四种方 ...

  9. 下面介绍mysql中模糊查询的四种用法:

    下面介绍mysql中模糊查询的四种用法: 1,%:表示任意0个或多个字符.可匹配任意类型和长度的字符,有些情况下若是中文,请使用两个百分号(%%)表示. 比如 SELECT * FROM [user] ...

随机推荐

  1. 利用modelarts和物体检测方式识别验证码

    近来有朋友让老山帮忙识别验证码.在github上查看了下,目前开源社区中主要流行以下几种验证码识别方式: tesseract-ocr模块: 这是HP实验室开发由Google 维护的开源 OCR引擎,内 ...

  2. NIO-Channel

    目录 NIO-Channel 目录 前言 什么是Channel 通道类型 如何使用 ServerSocketChannel SocketChannel FileChannel 总结 相关文献 NIO- ...

  3. POJ3111

    Demy has n jewels. Each of her jewels has some value vi and weight wi. Since her husband John got br ...

  4. 【JS】380- JavaScript 正则新特性

    概括 如果你曾用 JavaScript 进行过复杂的文本处理操作,那么你将会喜欢 ES2018 中引入的新特性.本文将详细介绍第9版标准如何提高 JavaScript 的文本处理能力. 大多数编程语言 ...

  5. 冒泡排序 C&&C++

    冒泡排序(因为过程像冒泡,所以叫做冒泡排序)   流程: (1)对数组中各个数字,一次比较相邻两个 (2)如果前面大于后面,就交换这两个数据 (3)再用同样的方法继续排,直到外层循环排完 或者 (1) ...

  6. 21.跨域和CORS

    一 跨域 同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响.可以说Web是构建在同源策略基础之上的 ...

  7. Java垃圾回收机制你还不明白?一线大厂面试必问的!

    什么是自动垃圾回收? 自动垃圾回收是一种在堆内存中找出哪些对象在被使用,还有哪些对象没被使用,并且将后者删掉的机制. 所谓使用中的对象(已引用对象),指的是程序中有指针指向的对象:而未使用中的对象(未 ...

  8. xshell连接问题记录

    操作系统Ubuntu 18.04 安装ubuntu后,连接不上.ubuntu18会每次重启重写dns,导致每次开机ip地址都不一样,所以需要先固定IP ubuntu18.04固定ip 修改固定 IP ...

  9. 跟着文档学习gulp1.2创建任务(task)

    导出任务 任务(task)可以分为公开(public)或私有(private)类型 公开任务从gulpfile中被导出(export),可以通过gulp命令直接调用 私有任务被设计为在内部使用,通常作 ...

  10. 五分钟了解ES6对数值的扩展

    文章目录 数值的扩展(ES6) 1. 二进制八进制表示法 2. Number对象 3. Math对象 4. 指数运算符 5. Integer 数据类型 5.1 简介 5.2 运算 数值的扩展(ES6) ...