译者:嘴里起了个泡
原文地址: wsvincent.com/javascript-…

这篇文章详细介绍了JS在执行for循环里面的 setTimeout() 语句的时候发什么了什么。这是面试中经常会被问到的一个问题,因为这个问题的答案涉及到了几个JS的核心知识点:闭包(closures),提升(hoisting)和事件循环(the event loop)。

For循环

For循环是JS开发中经常使用的。它会一直运行直到其中的判断条件为false。一个For循环包含三个分句:一个初始化表达式,一个条件表达式和一个更新表达式。

for (var i = 1; i < 5; i++) {console.log(i);  // 1 2 3 4
}
复制代码

现在我们的三个分句如下:

  • 初始化:var i = 1
  • 条件: i < 5
  • 更新:i++

需要注意的是在这个for循环结束的时候,变量i的值实际上是5,不是4。我们从初始化开始,每次i递增1,然后检查i是否满足条件。换句话说,我们会按照1,3,2的顺序执行这三个分句,尽管逻辑上会认为它们应该按顺序执行。
让我们来检查一下for循环里实际发生了什么:

  • 第一步:i值为1,增加到2,检查2 < 5?满足条件,所以打印输出。
  • 第二步:i值为2,增加到3,检查3 < 5?满足条件,所以打印输出。
  • 第三步:i值为3,增加到4,检查4 < 5?满足条件,所以打印输出。
  • 第四步:i值为4,增加到5,检查5 < 5?不满足条件,终止循环。
    现在我们清楚了,为什么i最终等于5,但是却只打印出来了1-4。我们可以通过下面代码来证明这点。
for (var i = 1; i < 5; i++) {console.log(i); // 1 2 3 4
}console.log("The value of i is now: ", i); // "The value of i is now: 5"
复制代码

闭包

关键字Var的作用域是函数范围内,意味着它位于一个封闭的函数中。但我们上面的例子中并没有函数,所以它的作用域就是全局。也就是说,上面的for循环创建了一个全局变量i
请注意,既然var的作用域是函数范围内,那么i的作用域就会被设置到离它最近的函数中。在这个例子中,它将会是全局变量。

setTimeout

如果我们想在循环中每秒输出一次应该怎么做呢?我们会想当然的认为只要添加一个setTimeout方法就能达到这个效果。

for (var i = 1; i < 5; i++) {setTimeout(() => console.log(i), 1000)  // 5 5 5 5
}
复制代码

事与愿违!!!为什么没有输出1 2 3 4呢?在这个微妙的例子中实际上发生了很多事情。
简单的回答就是for循环先执行掉了,然后再去寻找i的值,发现是5,然后把它打印了四次,每个循环打印一次。
即使我们把循环的时间间隔设置成0,结果还是一样的。

for (var i = 1; i < 5; i++) {setTimeout(() => console.log(i), 0)  // 5 5 5 5
}
复制代码

我相信你对此肯定很疑惑。不用担心:你很快就会知道这到底是怎么回事了。

JavaScript 运行引擎

JavaScript是单线程单一并发语言,这意味着它一次只能处理一个任务或一段代码。让我们接着看: 所以我们如何用它写出异步的代码呢,就比如上面例子中的setTimeout()? 答案是JavaScript运行在浏览器中,浏览器做了很多事情不仅仅是执行代码这么简单。事实上,浏览器需要考虑这四个部分:

  • JavaScript运行时引擎
  • 浏览器提供的Web APIs,比如DOM,setTimeout等等
  • 具有回调函数(如onClick和onLoad)的事件的回调队列
  • 事件循环

下面这个图片来自Philip Roberts’s fantastic talk on the Event Loop视频里的截图:

运行引擎执行我们的代码,每个浏览器都有一个稍微不同的引擎。例如,Chrome使用V8引擎,这也恰好为NodeJs提供支持。该引擎一次只能执行一段代码。
Web APIs是浏览器提供给我们的,其中包含了像setTimeout()这种方法。如果你在浏览器的控制台把window打印出来,你会看到一个很长很长的默认的API列表。

这些API是由浏览器在一个单独的进程里独立运行的。这就是JavaScript可以发生异步的原因!!! 并不是JavaScript本身可以同时做多件事;而是,浏览器可以同时为我们运行多个不同的进程。 到这里我希望你提出的问题是,运行引擎和Web API 是怎么样相互协同工作的?答案是通过回调队列事件循环
回调队列它是需要在JavaScript运行引擎中断后执行的一个任务队列。
事件循环是最后一个需要破解的谜团,它是一个不断运行的循环,用来连接堆栈和回调队列。 接下来让我们看一下它们在我们for循环和setTimeout的例子中是如何一起运行工作的。

闭包

setTimeout可以通过闭包拿到i的值。我们把i放在console.log语句里,但是i的值却被设置在外面一层的封闭范围内,即for循环里。既然内部函数可以拿到外部函数的变量,我们就能去for循环里取到i的值,即5。

[译]JS闭包:For循环中的setTimeout相关推荐

  1. js的for循环中return可以跳出循环,而使用forEach等不可以跳出循环

    描述 在vue项目中做表格增加行的时候,校验行内单元格某些字段的必填项,刚开始使用forEach进行循环遍历数组对象,然后判断对象的键值是否为空,为空则跳出循环,提示需要填写必填项,否则提示进行增加行 ...

  2. 彻底理解js闭包并应用,吊打面试官!

    目录 闭包 我的理解分为3点: 比较一下例1和例2 其实我们经常使用闭包 循环中闭包的使用 下面稍作修改就可以达到我们想要的效果 应用 欢迎指正!! 闭包 给人的感觉是很熟悉却又抓不到的感觉,都知道它 ...

  3. for循环中var和let的区别

    今天在做题的时候,遇到了一个不太懂的问题,经过查阅资料,终于搞明白了这个问题,发出来当做个笔记. 首先是var: for (var i = 0; i < 5; i++) { setTimeout ...

  4. JS闭包的理解及常见应用场景

    JS闭包的理解及常见应用场景 一.总结 一句话总结: 闭包是指有权访问另一个函数作用域中的变量的函数 1.如何从外部读取函数内部的变量,为什么? 闭包:f2可以读取f1中的变量,只要把f2作为返回值, ...

  5. Vue指令之v-for——迭代数组、迭代对象中的属性、迭代数字||v-for循环中key属性的使用

    v-for循环普通数组 v-for循环对象数组 v-for循环对象 v-for迭代数字 v-for循环中key属性的使用 <!DOCTYPE html> <html lang=&qu ...

  6. html页面如何循环遍历,遍历foreach循环中的HTML DOM元素

    5 个答案: 答案 0 :(得分:3) 您可以使用closest('tr')查找最近的父tr元素,然后使用index()获取行号.试试这个: $('td').click(function() { va ...

  7. 面试官:谈谈对JS闭包的理解及常见应用场景(闭包的作用)

    文章目录 对JS闭包的理解及常见应用场景(闭包的作用) 1.变量作用域 2.如何从外部读取函数内部的变量? 3.闭包概念 4.闭包用途 5.闭包的理解 6.闭包应用场景 setTimeout传参 回调 ...

  8. el-upload在表单循环中,实现选择,删除,拖拽,并且和表单数组对上

    el-upload在表单循环中,实现选择,删除,拖拽,并且和表单数组对上: 微信: volley369 业务要求,实现以下效果: 业务要求: el-upload肯定不能一个个的写,要用json去循环 ...

  9. Vue v-for循环中 key 属性的使用

    v-for循环中 key 属性的使用 使用到的方法 unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度. push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度. k ...

最新文章

  1. server sql 分组 去重 字符串拼接_SQL | 数据分析面试必备SQL语句+语法
  2. Python pickle使用
  3. Java中 String的反转
  4. SAP Spartacus的ComponentFactory和ComponentFactoryResolver - 动态添加outlet
  5. 数据可视化【五】 Scatter Plot
  6. php怎么实现简单的mvc框架,php实现简单的MVC框架实例
  7. 项目优化经验mdash;mdash;垃圾回收导致的性能问题[z]
  8. 《天天数学》连载26:一月二十六日
  9. JavaScript之Ajax(一篇入门Ajax就够了)
  10. 企业微信电脑版显示连不上服务器,钉钉环境部分Anroid手机,微信、企业微信pc版,登录失败的解决办法...
  11. ps端午节计算机辅助设计,PS教程:端午节的字体设计
  12. matlab泊松分布参数,MATLAB如何使用poisspdf函数计算泊松分布的概率
  13. Ubuntu22.04安装惠普P1108打印机并在局域网内共享
  14. html图片excel路径,jsp方式导出Excel能不能导出图片_html/css_WEB-ITnose
  15. 广告投放的相关名词CPM/CPT/CPC/CPD/CPI/CPS
  16. ent orm笔记2---schema使用(上)
  17. 仿微信视频通话大小视图切换(SurfaceView实现)
  18. Buffon投针实验 · 数学的直观理解 · 基础知识很重要
  19. Node的json解析
  20. 业余爱好者,学习JAVA在实际中有什么用途?

热门文章

  1. Python 基础课程第五天
  2. 电平转换与总线收发器详细介绍
  3. C 语言实例 - 计算 int, float, double 和 char 字节大小
  4. 【逆向知识】VS程序反汇编找main函数
  5. java 之UDP编程
  6. include查找文件路径
  7. hive中使用case、if:一个region统计业务(hive条件函数case、if、COALESCE语法介绍:CONDITIONAL FUNCTIONS IN HIVE)...
  8. 李航统计学习方法笔记第一章
  9. 命令关闭手机蓝牙_iPhone手机这些实用的便捷功能,你知道多少?
  10. endnote style_教你在论文写作中玩转 Endnote