setTimeOut和闭包
掘金上看到一个setTimeout与循环闭包的思考题。拿过来看了下,一方面了解settimeout的运行机制,还有就是js闭包的特性。关于闭包,有如下解释:
在这里写一点我对闭包的理解。理解闭包的关键在于:外部函数调用之后其变量对象本应该被销毁,但闭包的存在使我们仍然可以访问外部函数的变量对象。
function outer() {
var a = 1;
return function() {
return a;
};
}
var b = outer();
console.log(b()); //1
利用闭包,修改下面的代码,让循环输出的结果依次为1, 2, 3, 4, 5
for (var i=1; i<=5; i++) {
setTimeout( function timer() {
console.log(i);
}, i*1000 );
}
值得高兴的是很多朋友在读了文章之后确实对闭包有了更加深刻的了解,并准确的给出了几种写法。一些朋友能够认真的阅读我的文章并且一个例子一个例子的上手练习,这种认可对我而言真的非常感动。但是也有一些基础稍差的朋友在阅读了之后,对于这题的理解仍然感到困惑,因此应一些读者老爷的要求,借此文章专门对setTimeout进行一个相关的知识分享,愿大家读完之后都能够有新的收获。
在最初学习setTimeout的时候,我们很容易知道setTimeout有两个参数,第一个参数为一个函数,我们通过该函数定义将要执行的操作。第二个参数为一个时间毫秒数,表示延迟执行的时间。
setTimeout(function() {
console.log('一秒钟之后我将被打印出来')
}, 1000)

可能不少人对于setTimeout的理解止步于此,但还是有不少人发现了一些其他的东西,并在评论里提出了疑问。比如上图中的这个数字7,是什么?
每一个setTimeout在执行时,会返回一个唯一ID,上图中的数字7,就是这个唯一ID。我们在使用时,常常会使用一个变量将这个唯一ID保存起来,用以传入clearTimeout,清除定时器。
var timer = setTimeout(function() {
console.log('如果不清除我,我将会一秒之后出现。');
}, 1000)
clearTimeout(timer); // 清除之后,通过setTimeout定义的操作并不会执行
接下来,我们还需要考虑另外一个重要的问题,那就是setTimeout中定义的操作,在什么时候执行?为了引起大家的重视,我们来看看下面的例子。
var timer = setTimeout(function() {
console.log('setTimeout actions.');
}, 0);
console.log('other actions.');
// 思考一下,当我将setTimeout的延迟时间设置为0时,上面的执行顺序会是什么?
在浏览器中的console中运行试试看,很容易就能够知道答案,如果你没有猜中答案,那么我这篇文章就值得你点一个赞了,因为接下来我分享的小知识,可能会在笔试中救你一命。
在对于执行上下文的介绍中,我与大家分享了函数调用栈这种特殊数据结构的调用特性。在这里,将会介绍另外一个特殊的队列结构,页面中所有由setTimeout定义的操作,都将放在同一个队列中依次执行。
我用下图跟大家展示一下队列数据结构的特点。

而这个队列执行的时间,需要等待到函数调用栈清空之后才开始执行。即所有可执行代码执行完毕之后,才会开始执行由setTimeout定义的操作。而这些操作进入队列的顺序,则由设定的延迟时间来决定。
因此在上面这个例子中,即使我们将延迟时间设置为0,它定义的操作仍然需要等待所有代码执行完毕之后才开始执行。这里的延迟时间,并非相对于setTimeout执行这一刻,而是相对于其他代码执行完毕这一刻。所以上面的例子执行结果就非常容易理解了。
为了帮助大家理解,再来一个结合变量提升的更加复杂的例子。如果你能够正确看出执行顺序,那么你对于函数的执行就有了比较正确的认识了,如果还不能,就回过头去看看其他几篇文章。
setTimeout(function() {
console.log(a);
}, 0);
var a = 10;
console.log(b);
console.log(fn);
var b = 20;
function fn() {
setTimeout(function() {
console.log('setTImeout 10ms.');
}, 10);
}
fn.toString = function() {
return 30;
}
console.log(fn);
setTimeout(function() {
console.log('setTimeout 20ms.');
}, 20);
fn();

OK,关于setTimeout就暂时先介绍到这里,我们回过头来看看那个循环闭包的思考题。
for (var i=1; i<=5; i++) {
setTimeout( function timer() {
console.log(i);
}, i*1000 );
}
如果我们直接这样写,根据setTimeout定义的操作在函数调用栈清空之后才会执行的特点,for循环里定义了5个setTimeout操作。而当这些操作开始执行时,for循环的i值,已经先一步变成了6。因此输出结果总为6。而我们想要让输出结果依次执行,我们就必须借助闭包的特性,每次循环时,将i值保存在一个闭包中,当setTimeout中定义的操作执行时,则访问对应闭包保存的i值即可。
而我们知道在函数中闭包判定的准则,即执行时是否在内部定义的函数中访问了上层作用域的变量。因此我们需要包裹一层自执行函数为闭包的形成提供条件。
因此,我们只需要2个操作就可以完成题目需求,一是使用自执行函数提供闭包条件,二是传入i值并保存在闭包中。
for (var i=1; i<=5; i++) {
(function(i) {
setTimeout( function timer() {
console.log(i);
}, i*1000 );
})(i)
}

当然,也可以在setTimeout的第一个参数处利用闭包。
for (var i=1; i<=5; i++) {
setTimeout( (function(i) {
return function() {
console.log(i);
}
})(i), i*1000 );
}
setTimeOut和闭包的更多相关文章
- setTimeout 学习闭包
@(技术笔记)[css] 学习参考网站 css 网站,可供参考 javascript学习网站 var create = function (i){ return function(){ console ...
- setTimeout使用闭包功能,实现定时打印数值
我们这次使用setTimeout来实现一个按照时间定时,依次打印数值的例子.其实在早期的时候,也是我经常犯的一个错误,或者实现这种能力,似乎js比较牵强,其实是我的错,哈哈!没能理解JS强大之处.我们 ...
- 当setTimeout遇到闭包
1: function myTest(){ for(var i=0; i< 5; i++){ setTimeout(console.log(i), 0); } } myTest(); 或者比较正 ...
- setTimeout 与 闭包。。。
先看下面一个比较坑的代码 for (var i=1; i<=5; i++) { setTimeout( function timer() { console.log(i); }, i*1000 ...
- 由一道面试题简单扩展 — setTimeout、闭包
在一个前端公众号,看到这么一个号称简单的面试题: 1.以下程序输出什么? <script type="text/javascript"> function init() ...
- 附件1:setTimeout与闭包
我在详细图解作用域链与闭包一文中的结尾留下了一个关于setTimeout与循环闭包的思考题. 利用闭包,修改下面的代码,让循环输出的结果依次为1, 2, 3, 4, 5 for (var i=1; i ...
- js闭包(三)
场景一:采用函数引用方式的setTimeout调用 闭包的一个通常的用法是为一个在某一函数执行前先执行的函数提供参数.例如,在web环境中,一个函数作为setTimeout函数调用的第一个参数,是一种 ...
- Web前端-JavaScript基础教程上
Web前端-JavaScript基础教程 将放入菜单栏中,便于阅读! JavaScript是web前端开发的编程语言,大多数网站都使用到了JavaScript,所以我们要进行学习,JavaScript ...
- 菜鸟凉经(华为、firehome、大华)
面试通知都是前一天来的,准备的时间很少,所以表现也不是特别满意,来看面经吧: 华为一面(IT应用工程师): 1.自我介绍:(华为面试都是1对1,面前的是个温柔的小哥,挺放松的) 2.你主要会的it技术 ...
随机推荐
- react项目开发入门
v16.2.0 在html头部引入react相关js文件 <!-- react核心库--><script src="../static/react/react.produc ...
- centos设置时间同步
1.安装ntpdate #yum install ntpdate 2. #cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime #ntpdate ...
- COM 自动化控制Excel应用程序
class Program { static void Main(string[] args) { var dt = new System.Data.DataTable(); dt.Columns.A ...
- An Introduction to Lock-Free Programming
Lock-free programming is a challenge, not just because of the complexity of the task itself, but bec ...
- 升级到EFCore2.0
EF Core 2.0上周已经发布了 文章内容基于vs2017,请大家先安装好vs2017(15.3). 本篇文章主要讲下差异点,跟之前一样的就不再重复了. 文章目录(差异点): 一.新建项目, EF ...
- [HNOI2008]GT考试 矩阵优化DP
---题面--- 题解: 一开始看觉得很难,理解了之后其实还挺容易的. 首先我们考虑朴素DP: 令f[i][j]表示长串到了第i项, 与不吉利数字(模式串)匹配到了第j项的方案. 显然ans = f[ ...
- Android 内核--Binder架构分析
一.Binder架构 在Android中,Binder用于完成进程间通信(IPC),即把多个进程关联在一起.比如,普通应用程序可以调用音乐播放服务提供的播放.暂停.停止等功能.Binder工作在Lin ...
- odex文件格式
apk安装或启动时,会通过dexopt来将dex生成优化后的odex文件.过程是将apk中的classes.dex解压后,用dexopt处理并保存为“/data/dalvik-cache/data@a ...
- 【贪心】【CF3D】 Least Cost Bracket Sequence
传送门 Description 给一个序列,序列里面会有左括号.问号.右括号.对于一个\(?\)而言,可以将其替换为一个\((\),也可以替换成一个\()\),但是都有相应的代价.问:如何替换使得代价 ...
- Spring源码解析-autowiring自动装配的实现
IoC容器提供了自动依赖装配的方式,为应用IoC容器提供很大的方便.在自动配置中,不需要显式的去指定Bean属性,只需要配置autowiring属性,IoC容器会根据这个属性配置,使用反射的方式查找属 ...