for循环中let,var 的经典面试题:for循环中 console.log(i)详解

同学们在刚准备面试时肯定见过一道经典面试题:

for(var i = 0; i < 10; i++) {setTimeOut(function(){console.log(i)})
}
// 输出 10 10 10 10 10 10 10 10 10 10for(let i = 0; i < 10; i++) {setTimeOut(function(){console.log(i)})
}
// 输出 0 1 2 3 4 5 6 7 8 9

有的同学就很疑惑,怎么肥事,这不一样嘛!

在这里插入图片描述
然后就去百度查查
在这里插入图片描述
然后百度就会告诉你:

第一个变量i是用var声明的,在全局范围内有效,所以全局中只有一个变量i,每次循环时,setTimeOut定时器里指的是全局变量i,而循环里的十个setTimeOut是在循环结束后才执行,所以输出十个10。
第二个变量i是用let声明的,当前的i
只在本轮循环中有效,每次循环的i其实都是一个新的变量,所以setTImeOut定时器的里面的i其实不是同一变量,所以输出0123456789

看完以后
在这里插入图片描述
似懂非懂

就算使用var定义的i是全局变量,每次循环都改变全局范围里的i的值,但是循环一次,执行一次setTimeOut啊,不也应该输出当前i值吗?

em…

不管,面试官问我我就照着这么回答就行了,面试官肯定明白我说的什么
在这里插入图片描述
其实之前我理解的也是很片面的,直到这两天看见一篇Google大佬的文章,全是英文,看了好半天****,下面👇,我就结合这篇文章给大家讲讲我的理解(瞎讲讲)吧
在这里插入图片描述

首先,在理解这个问题之前,我们需要理解一下macro-task(宏任务)和micro-task(微任务)

先给大家举一个例子(大佬举的例子):

console.log('script start');setTimeout(function() {console.log('setTimeout');
}, 0);Promise.resolve().then(function() {console.log('promise1');
}).then(function() {console.log('promise2');
})console.log('script end');

大家来仔细猜一猜执行结果是什么呐?

结果:
script start
script end
promise1
promise2
setTimeout

在这里插入图片描述

嘿!有点东西

其实js是单线程的,每个线程有它自己的唯一的事件循环,但是事件循环的任务源可以不唯一。类似setTimeout, promise, ajax, DOM操作等都是典型的任务源,任务队列中的任务便是来自这些任务源。而这些任务源产生的任务又可以分为macro-task(宏任务)和micro-task(微任务)两种。

macro-task(宏任务)

macro-task(宏任务)中的任务都是有时间顺序的,因此浏览器能够有序地从中调度任务并执行。在任务与任务之间,浏览器可能会渲染更新。
macro-task(宏任务)中一个典型就是setTimeout,setTimeout函数等待给定的延迟事件然后将其回调函数推入宏任务Event Queue中。这就是为什么先输出’script end’ 后输出’setTimeout’的原因。
macro-task(宏任务)主要有:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。

micro-task(微任务)

micro-task(微任务)中的任务在当前函数调用栈中的函数执行完成之后即调度,像promise、mutation都会被推入微任务Event Queue队列中。并且微任务Event Queue队列中的一个任务执行完成后,后续的micro-task(微任务)也会继续执行,直到微任务Event Queue队列为空,这就解释了为什么promise2也会在setTimeout之前输出的原因。
微任务Event Queue队列主要有process.nextTick, Promise, Object.observe(已废弃), MutationObserver(html5新特性)

当宏任务Event Queue队列中的一个任务执行结束时,如果函数调用栈为空,便会开始执行微任务Event Queue队列中的任务,直至微任务Event Queue队列中所有任务执行完毕,然后event loop才会继续执行宏任务Event Queue队列中的下一个任务。

上图文:
在这里插入图片描述

接下来,上个厉害的(也是大佬举的例子)

<div class="outer"><div class="inner"></div>
</div>

分别给这两个div加上点击事件


var outer = document.querySelector('.outer');
var inner = document.querySelector('.inner');new MutationObserver(function () {console.log('mutate');
}).observe(outer, {attributes: true,
});function onClick() {console.log('click');setTimeout(function () {console.log('timeout');}, 0);Promise.resolve().then(function () {console.log('promise');});outer.setAttribute('data-random', Math.random());
}inner.addEventListener('click', onClick);
outer.addEventListener('click', onClick);

大家再来仔细思考一下执行结果是什么呐?

结果:
click
promise
mutate
click
promise
mutate
timeout
timeout

在这里插入图片描述
嘿!有点大东西

上述例子中Dispatch click和setTimeout属于宏任务Event Queue,对应的回调函数被推进宏任务Event Queue队列中,Mutation observer和promise属于微任务Event Queue,对应的回调函数则被推入微任务Event Queue队列中。当点击inner元素时,代码执行执行过程如下所示

  1. Dispatch click被推入宏任务Event Queue队列中,当点击inner元素时onClick被推入函数调用栈(Js stack)中,执行上下文进入onClick中,将setTimeout的回调函数推入宏任务Event Queue队列中,Mutation observers和Promise then的回调函数推入微任务Event Queue队列中,并执行输出click。
    在这里插入图片描述

  2. 当onClick执行结束后, 函数调用栈为空,将微任务Event Queue队列中的 promise then 的回调函数推入函数调用栈。
    在这里插入图片描述

  3. 同样地,当promise then的回调函数执行结束后,将mutation observers的回调函数推入函数调用栈
    在这里插入图片描述

  4. 由于事件冒泡机制,父元素outer也会响应点击事件,因此重复1-3步骤,执行结束后如下所示。
    在这里插入图片描述

  5. 此时函数调用栈和microtasks中均为空,因此event loop将执行tasks中的下一个任务。
    在这里插入图片描述

  6. 再执行tasks中的最后一个任务。
    在这里插入图片描述
    (注:以上例子均在谷歌浏览器中的输出结果)

上图文:
在这里插入图片描述

把这两个例子理解好,是不是感觉对 这道经典面试题 豁然开朗,甚至有点小菜一碟了呐、
在这里插入图片描述
其实最后总结一下就是:
当浏览器在执行这段

for(var i = 0; i < 10; i++) {setTimeOut(function(){console.log(i)})
}

代码时
先在全局定义变量 i, 然后执行 for 循环,执行一次 for 循环,分别将 i++ 放入函数调用栈队列,setTimeout 放入task队列 一次。
因为需要将函数调用栈队列里的任务执行结束后,再往下执行task任务
所以 i++ 一直在执行,10次 i++ 执行结束, i 的值为10(为什么不是9?因为 i++ 在值为9时,还会进行一次i++操作,最后一个循环完 i 的值为10,不满足条件,不再循环)。
至此,函数调用栈队列任务执行结束,再去执行task里的十个setTimeout任务,2而此时 i 的值为10,所以输出10 个 10。

最后 附上 :Google 大佬文章

感谢大佬!虽然我们只是大佬的搬运工,但是希望在搬运之前理解好我们所搬运的内容,这已经很厉害啦。

以上理解若有偏差,望各位同学们批评指正。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/515512.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

后疫情时代,银行从数字化转型到智能化“迁徙”

云栖号资讯&#xff1a;【点击查看更多行业资讯】 在这里您可以找到不同行业的第一手的上云资讯&#xff0c;还在等什么&#xff0c;快来&#xff01; 全球数据智能趋势一览 笔者在搜索了众多机构发表的数据智能发展趋势报告&#xff0c;并做了筛选和甄别后&#xff0c;参考了公…

普通二本学校软件工程专业本科毕业的女生,没有考研,选择直接就业现如今过得怎样呐?

第一篇程序人生 在进入大学之前买的联想笔记本电脑被我之前放在窗户边&#xff0c;一个月之前去上班的时候忘记关窗户&#xff0c;下大雨给淋雨进水了&#xff0c;刚好开机密码的几个键盘失灵了&#xff0c;上周末在网上买了一个键盘&#xff0c;终于可以开机了&#xff0c;为…

阿里云交通数据中台解决方案打造“数字化生产力”

数字经济时代&#xff0c;计算、分析、处理等作为“关键生产要素”已成为行业和社会的共识。但是对于交通领域而言&#xff0c;以往端到端的方式进行平台搭建和应用开发已不能适应数字爆炸和产品快速迭代的要求。交通行业在计算分析方面面临着信息采集难、样式杂、变化快、价值…

一次讲清楚,七种分布式事务的解决方案

来源 | moon聊技术责编 | 寇雪芹头图 | 下载于视觉中国什么是分布式事务分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器「分别位于不同的分布式系统的不同节点之上」。一个大的操作由N多的小的操作共同完成。而这些小的操作又分布在不同的服务上。针…

SpringCloud应用在Kubernetes上的最佳实践—开发篇

作者 | 孤弋 阿里云高级技术专家&#xff0c;负责 EDAS 的开发和用户体验优化工作。 前言 近年来&#xff0c;云原生、Kubernetes、微服务、SpringCloud 这些名词在技术圈内不绝于耳&#xff0c;数据显示&#xff0c;使用 SpringCloud 作为微服务的框架&#xff0c;同时选择…

支持批任务的Coscheduling/Gang scheduling

作者&#xff1a;王庆璨 张凯 进击的Kubernetes调度系统&#xff08;一&#xff09;&#xff1a;Scheduling Framework 进击的Kubernetes调度系统&#xff08;二&#xff09;&#xff1a;支持批任务的Coscheduling/Gang scheduling 前言 首先我们来了解一下什么是Coscheduli…

ESLint is disabled since its execution has not been approved or denied yet

我的vs code有安装eslint插件&#xff0c;但是不这道为什么这两天很多代码校验都不起作用了 一顿操作猛如虎&#xff0c;最后发现代码开始的时候有一条黄线 爆出了一个错误 ESLint is disabled since its execution has not been approved or denied yet. Use the light bulb…

13种重要的云原生工具,让交付过程更快

来源 | SDNLAB责编 | 寇雪芹头图 | 下载于视觉中国SUSE收购RancherPure Storage收购PortworxVeeam收购KastenVMware收购OctarineMirantis收购Lens IDE思科收购Banzai CloudNew Relic收购Pixie Labs云原生市场趋于整合........以上是过去一年云原生行业发生的并购案&#xff0c;…

后疫情时代企业将加速向云服务迁移

云栖号资讯&#xff1a;【点击查看更多行业资讯】 在这里您可以找到不同行业的第一手的上云资讯&#xff0c;还在等什么&#xff0c;快来&#xff01; LogicMonitor的新调查结果表明&#xff0c;COVID-19已成为企业快速实施云迁移的强大催化剂&#xff0c;尽管整体情况仍在发展…

会话拦截限制一台手机登录

文章目录1. 流程图2. 流程简述1. 流程图 2. 流程简述 1.前端传递userId和token2.后端接收userId和token3.校验userId和token是否为空4.校验任一为空&#xff0c;则提示“请登录后再继续操作&#xff01;”5.不为空&#xff0c;通过UserId从redis中获取token6.redis中的token与…

HTML a链接下载文件之图片,文件,乱码等问题

我们在做需求的时候&#xff0c;经常会遇到下载文件 前端下载文件一般分为两种方式&#xff1a; 使用 a 链接进行下载&#xff1a; <a herf"url" >下载</a>向后端发送请求进行下载&#xff1a; methods:{downloadReport(item,index){let date item.…

冗余云计算连接:保持组织运行

云栖号资讯&#xff1a;【点击查看更多行业资讯】 在这里您可以找到不同行业的第一手的上云资讯&#xff0c;还在等什么&#xff0c;快来&#xff01; 在过去的几个月中&#xff0c;人们目睹了组织将其劳动力转移到“在家工作”模式和远程操作的情况。微软公司报告其云计算服务…

杭州湾跨海大桥视频上云,夯实智慧高速“云基建

现阶段&#xff0c;高速公路监控设备覆盖已经有普遍提高&#xff0c;但监控设施的覆盖仅完成了视频的监控功能&#xff0c;没有实现分析提醒功能。传统人工巡检方式工作量大且容易发生事件遗漏、事故发现不及时导致二次事故、人工视频巡检工作量大无法做到实时检测、监控中心绝…

移动端一键登录注册

文章目录1. 用户名密码一键注册登录流程2. 手机号一键注册登录流程1. 用户名密码一键注册登录流程 2. 手机号一键注册登录流程

使用html2Canvas将页面转化为canvas图片,最后长按保存到本地,史上最全 html2canvas 使用 踏坑之旅,没有之一

最近工作中遇到一个需求&#xff0c;类似这样 点击商品二维码&#xff0c;生成一张带有商品图片、标题、描述、二维码等信息的图片&#xff0c;用户长按进行保存。 在使用html2canvas进行项目开发的时候&#xff0c;遇到很多的问题&#xff0c;主要为一下方面&#xff1a; 1、…

饿了么4年+阿里2年:研发路上的一些总结与思考

我是在2014年入职饿了么&#xff0c;从前端和PHP一直做到后端架构和团队&#xff0c;从2014年到2017年陆续负责过公司客服、销售、代理商、支付、清结算、订单这些业务的产研与团队&#xff1b;2018年从业务研发团队抽身&#xff0c;6个人组起一个小组投身机器学习&#xff0c;…

到底谁在使用低代码?钉钉低代码用户画像:非IT人员占8成

编辑 | 宋慧 供稿 | 钉钉 头图 | 付费下载于视觉中国 低代码开发需求到底有多大&#xff1f;谁在使用低代码开发&#xff1f;3月2日&#xff0c;钉钉发布低代码开发者画像&#xff1a;一二线城市的80、90后是低代码开发的主力军&#xff0c;但20岁以下和50岁以上开发者也占比近…

都已经十岁的ApacheDubbo,还能再乘风破浪吗?

云栖号资讯&#xff1a;【点击查看更多行业资讯】 在这里您可以找到不同行业的第一手的上云资讯&#xff0c;还在等什么&#xff0c;快来&#xff01; 纵观中国开源历史&#xff0c;你真的没法找到第二个像 Dubbo 一样自带争议和讨论热度的开源项目。 一方面&#xff0c;2011 …

前后端分离,如何解决跨域(代理模式)、路由拦截(进入页面需要登录)以及请求拦截(登录TOKEN失效)等问题(初学者)

前端时间项目需要发布一个较大的版本&#xff0c;工作比较忙&#xff0c;加了好多个晚上的班&#xff0c;感觉自己有点缺氧了。最近稍微闲下来了&#xff0c;顺便调休了三天&#xff0c;刚刚给家里来了个大扫除&#xff0c;看着这干干净净的小家&#xff0c;心里顿时舒服了很多…

秒懂云通信:通信圈黑话大盘点

原文链接 本文为云栖社区原创内容&#xff0c;未经允许不得转载。