有趣的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做 ...
随机推荐
- CodeForces - 438D: The Child and Sequence(势能线段树)
At the children's day, the child came to Picks's house, and messed his house up. Picks was angry at ...
- c# Http请求之HttpClient
利用HttpClient进行Http请求,基于此,简单地封装了下: using System; using System.Collections.Generic; using System.Colle ...
- [独孤九剑]Oracle知识点梳理(三)导入、导出
本系列链接导航: [独孤九剑]Oracle知识点梳理(一)表空间.用户 [独孤九剑]Oracle知识点梳理(二)数据库的连接 [独孤九剑]Oracle知识点梳理(三)导入.导出 [独孤九剑]Oracl ...
- 理解SQL查询的底层原理
阅读目录 一.SQL Server组成部分 二.查询的底层原理 本系列[T-SQL]主要是针对T-SQL的总结. T-SQL基础 [T-SQL基础]01.单表查询-几道sql查询题 [T-SQL基础] ...
- 一次调用cloudera的的API和JSON想到的
JSON的null字段 在我的项目中修改了YARN的资源池,到cloudera中就报错,查询还不报错,但是修改的时候,就报错,大致意思就是信息有异常,“包含了尖括号. 因为通过API+json交互,我 ...
- 洛谷【P1616】疯狂的采药
浅谈\(DP\):https://www.cnblogs.com/AKMer/p/10437525.html 题目传送门:https://www.luogu.org/problemnew/show/P ...
- jenkins jacoco
1.pom中加jacoco依赖 <dependency> <groupId>org.jacoco</groupId> <artifactId>jacoc ...
- vijos1369:难解的问题
描述 在你的帮助下,蔚蓝来到了埃及.在金字塔里,蔚蓝看到了一个问题,传说,能回答出这个问题的人就能受到埃及法老的祝福,可是蔚蓝日夜奋战,还是想不出来,你能帮帮他么?(XXX: 胡扯,教主怎么可能想不出 ...
- vector向量容器元素排序与查找
1.利用标准库函数sort()对vector进行排序 参考源码: #include <algorithm> #include <vector> vector<int> ...
- Ubuntu16.04+TensorFlow r1.12环境搭建指南
一.操作系统安装 OS版本:Ubuntu 16.04 (ubuntu-16.04.5-server-amd64.iso) CPU:4Core以上 内存:4GB以上 磁盘空间:80G以上 二.基础环境准 ...