有趣的setTimeout
今天在回顾JavaScript进阶用法的时候,发现一个有趣的问题,话不多说,先上代码:
for(var j=0;j<10;j++){
setTimeout(function(){console.log(j)},5000)
}
看到这三行代码,也许你会不耐烦道:又要讲闭包?要吐了好么?别急,让我们先来思考一下,这段代码在浏览器中的执行结果是什么?
执行结果显示,浏览器打印出了十个10

家都知道,JavaScript在ES6出现以前,是没有块状作用域的,这就意味着, 在for循环中用var定义的变量j,其实是属于全局的,即在全局范围内都可以被访问到,既然如此,那其实整个全局作用域中就只有一个j,每次for循环都i是在更新这个j。
那么现在关键的问题在于,为什么整个for循环会先于setTimeout执行,而不是我们正常理解的,一次迭代执行一次。
这就涉及到了JavaScript的核心特性:单线程。
JavaScript设计的初衷,是浏览器用来与用户进行交互和DOM操作的。这就决定了它必须是单线程的,设想JavaScript同事有两个线程,一个线程在DOM节点添加内容,一个线程删除该节点,浏览器就会出现混乱。所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
为了优化单线程的性能,JavaScript将任务分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有主线程中的同步任务执行完毕,异步任务才会进入执行队列执行。只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制。这个过程会不断重复。
而setTimeout,就被JavaScript定义为异步任务。每次for循环的迭代,都将setTimeout中的回调函数加入任务队列等待执行。也就是说,只有同步任务中的for循环完全结束,主线程中才会去任务队列中找到尚未执行的十个setTimeout(十次迭代)回调函数并顺序执行(先进先出)。而此时,i已经经过循环结束变成了10,所以,此时主线程执行的,是十个一摸一样的打印i的回调函数,即打印十个10。至此就完美回答了第一和第二个问题,文章开头的代码与下面的代码其实是等价的:
for(var i=0;i<10;i++){}
setTimeout(console.log(i),5000)
setTimeout(console.log(i),5000)
setTimeout(console.log(i),5000)
setTimeout(console.log(i),5000)
setTimeout(console.log(i),5000)
setTimeout(console.log(i),5000)
setTimeout(console.log(i),5000)
setTimeout(console.log(i),5000)
setTimeout(console.log(i),5000)
setTimeout(console.log(i),5000)
有趣的setTimeout的更多相关文章
- 你不知道的Javascript:有趣的setTimeout
你不知道的Javascript:有趣的setTimeout 有时候,小小的细节往往隐藏着大大的智慧今天在回顾JavaScript进阶用法的时候,发现一个有趣的问题,话不多说,先上代码: for(var ...
- 每个JavaScript工程师都应懂的33个概念
摘要: 基础很重要啊! 原文:33 concepts every JavaScript developer should know 译文:每个 JavaScript 工程师都应懂的33个概念 作者:s ...
- 每个 JavaScript 工程师都应懂的33个概念
简介 这个项目是为了帮助开发者掌握 JavaScript 概念而创立的.它不是必备,但在未来学习(JavaScript)中,可以作为一篇指南. 本篇文章是参照 @leonardomso 创立,英文版项 ...
- 【小贴士】关于transitionEnd/animate的一个有趣故事
前言 在很久之前,我们项目有一个动画功能,功能本身很简单,便是典型的右进左出,并且带动画功能 以当时来说,虽然很简单,但是受限于框架本身的难度,就直接使用了CSS3的方式完成了功能 当时主要使用tra ...
- js中setTimeout()时间参数设置为0的探讨
起因源于一道前端笔试题: var fuc = [1,2,3]; for(var i in fuc){ setTimeout(function(){console.log(fuc[i])},0); co ...
- setTimeout 第三个参数 改变setTimeout的作用对象 控制下拉框的关闭
setTimeout第三个参数,可以作为setTimeout延时执行函数的传入参数使用,利用这个设定,我们可以将要延时改变状态的对象传入,变相改变setTimeout的作用对象:这里setTimeou ...
- 浅谈setTimeout函数和setInterval函数
前几天学了js,看到了两个非常有趣的函数,他们分别是setTimeout函数和setInterval函数,这两个函数能使网页呈现非常一些网页中比较常见的效果,比如说图片轮播,等一些非常好玩的效果.下面 ...
- 有趣的win8进度条
有趣的win8进度条 刚才在安装visual studio 12,发现它的安装界面都是win8风格的,而且安装的时候有个进度条,看着挺不错,就用 jquery 实现了一下,的确挺有趣: 点击停止效果 ...
- setTimeout与setInterval参数之String
今天无意中给某网友解答了一些setTimeout的问题,发现一个有趣的东西. 以前我总认为setTimeout的第一个参数只能function,后面发现string也能执行.那问题来了,String做 ...
随机推荐
- Number Sequence (KMP的应用)
个人心得:朴素代码绝对超时,所以要用到KMP算法,特意了解了,还是比较抽象,要多体会 Given two sequences of numbers : a11, a22, ...... , aNN, ...
- 关于overflow:hidden
(本文只针对hidden这个值的用处进行阐述) 关于overflow:hidden;很多人都知道他是溢出隐藏的一个属性,但是并不是很多人知道它的一些神奇的地方!首先先讲一下众所周知的溢出隐藏吧! 溢出 ...
- 皮肤和DLL和图片等项目文件完全整合到exe中
C#开发的程序原生界面实在是太丑了,自己又没有美化天赋,所以只能使用皮肤控件了,网上找到了IrisSkin2,包含一个.dll文件和若干ssk后缀的皮肤文件,代码其实很简单的.但是后来发现个问题,就是 ...
- 在.net中读写config文件的各种方法(自定义config节点)
http://www.cnblogs.com/fish-li/archive/2011/12/18/2292037.html 阅读目录 开始 config文件 - 自定义配置节点 config文件 - ...
- hdu 3932 Groundhog Build Home——模拟退火
题目:http://acm.hdu.edu.cn/showproblem.php?pid=3932 注意平均值与最远的点距离为0的情况.所以初值设成-1,这样 id 就不会乱.不过设成0也可以.注意判 ...
- 免费的xshell下载
平时从网站上下载的xshell,都是有有效期的,用一个月就生效了 所以我们可以这样: 学习源头: https://blog.csdn.net/zhoukikoo/article/details/794 ...
- Web中的安全性问题
根据2010年OWASP发布的Web应用程序安全风险主要是SQL注入攻击.跨网站脚本.伪造客户端请求.Cookie盗取,传输层保护不足. 1 SQL注入攻击 随着B/S框架结构在系统开发中的广泛应用 ...
- Java-API:java.util.regex.Pattern
ylbtech-Java-API:java.util.regex.Pattern 1.返回顶部 2.返回顶部 3.返回顶部 4.返回顶部 5.返回顶部 1. https://docs. ...
- 2014.2.23 datagridview显示图片的方法
DataTable dt = new DataTable(); dt.Columns.Add("", typeof(byte[])); dt.Rows.Add(File.ReadA ...
- svn 撤销修改
1,更新到最新版本 不知道是哪个文件的问题时,可以svn log查看日志,基本上可以通过哪一次提交修改了哪一个文件推算出 是哪个文件的问题. 若知道是哪个文件的问题,直接svn log 文件名 大致确 ...