for循环 + setTimeout 结合的烂大街的面试题
一、背景
最近在翻看以前的老书《node.js开发指南》,恰好碰到 for 循环 + setTimeout 的经典例子,于是重新梳理了思路并记录下。
二、写在前面,setTimeout 和 setInterval 的执行机制
在日常编码中,你会发现,给 setTimeout 和 setInterval 设定延迟时间往往并不准,或者干脆 setTimeout(function(){xxx},0) 也不是立马执行(特别是有耗时代码在前),这是因为 js 是单线程的,有一个事件队列机制,setTimeout 和 setInterval 的回调会到了延迟时间塞入事件队列中,排队执行。
setTimeout :延时 delay 毫秒之后,啥也不管,直接将回调函数加入事件队列。
setInterval :延时 delay 毫秒之后,先看看事件队列中是否存在还没有执行的回调函数( setInterval 的回调函数),如果存在,就不要再往事件队列里加入回调函数了。
看下面示例:
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
结果:1 秒之后,同时输出 5 个 5。
因为 for 循环会先执行完(同步优先于异步优先于回调),这时五个 setTimeout 的回调全部塞入了事件队列中,然后 1 秒后一起执行了。
三、正文
接下来就是那道经典的代码:
for (var i = 0; i < 5; i++) {
setTimeout(function (){
console.log(i);
},1000);
}
结果:5 5 5 5 5
为什么不是 1 2 3 4 5,问题出在作用域上。
因为 setTimeout 的 console.log(i); 的i是 var 定义的,所以是函数级的作用域,不属于 for 循环体,属于 global。等到 for 循环结束,i 已经等于 5 了,这个时候再执行 setTimeout 的五个回调函数(参考上面对事件机制的阐述),里面的 console.log(i); 的 i 去向上找作用域,只能找到 global下 的 i,即 5。所以输出都是 5。
解决办法:人为给 console.log(i); 创造作用域,保存i的值。
解决办法一
for (var i = 0; i < 5; i++) {
(function(i){ //立刻执行函数
setTimeout(function (){
console.log(i);
},1000);
})(i);
}
这里用到立刻执行函数。这样 console.log(i); 中的i就保存在每一次循环生成的立刻执行函数中的作用域里了。
解决办法二
for (let i = 0; i < 5; i++) { //let 代替 var
setTimeout(function (){
console.log(i);
},1000);
}
let 为代码块的作用域,所以每一次 for 循环,console.log(i); 都引用到 for 代码块作用域下的i,因为这样被引用,所以 for 循环结束后,这些作用域在 setTimeout 未执行前都不会被释放。
四、补充
在写示例代码的过程中,发现一个语法点:
function a(i){
console.log(i);
}
for (var i = 0; i < 5; i++) {
setTimeout(a(i),1000);
}
报错:
TypeError: "callback" argument must be a function
at setTimeout (timers.js:421:11)
……
百度了下,原来 setTimeout 不支持传带参数的函数,有两种解决方案:
(1)匿名函数包装
function a(i){
console.log(i);
}
for (var i = 0; i < 5; i++) {
setTimeout(function(){ //用匿名函数包装
a(i);
},1000);
}
(2)setTimeout 的第 3+ 个参数
setTimeout(func, delay, param1, param2, ...)
第三个参数及以后的参数都可以作为 func 函数的参数
function a(i){
console.log(i);
}
for (var i = 0; i < 5; i++) {
setTimeout(a,1000,i); //传入第3个参数
}
for循环 + setTimeout 结合的烂大街的面试题的更多相关文章
- 烂大街的 Spring 循环依赖问题,你觉得自己会了吗
文章已收录在 GitHub JavaKeeper ,N 线互联网开发.面试必备技能兵器谱,笔记自取. 微信搜「 JavaKeeper 」程序员成长充电站,互联网技术武道场.无套路领取 500+ 本电子 ...
- N厂水鬼烂大街?那来看ZF厂V4帝舵小红花
自从帝舵小红花推上市面之后,各大工厂都在推出新版本,但做得最成熟的还是ZF厂,帝舵这个品牌是非常低调的,很少有人关注,但是ZF厂在这款腕表也是下了不少功夫,曾经帝舵小红花和N厂水鬼并列为最顶级的表畅销 ...
- 大数据学习--day04(选择结构、循环结构、大数据java基础面试题)
选择结构.循环结构.大数据java基础面试题 switch: 注意: byte short int char String(jdk1.7支持) 不能是 long float double boolea ...
- for循环+setTimeout的延迟操作
例子: for (var i = 0; i < 5; i++) { setTimeout(function () { console.log(i); }, 100) } 上述代码,输出结果显而易 ...
- java程序员烂大街为何还不便宜?
最近跟一朋友聊天,他是做c#开发的.他答应了老板带领一帮java工程师开发网站.披星戴月终于搞定,现在已经盈利.但是他公司的那帮搞c#的同事不淡定了. 在招聘java程序员的时候2年有开15k的.5年 ...
- 约瑟夫环-循环队列算法(曾微软,google笔试题)
这也是我们聚会时常常做的游戏之一. 算法思路: 此处我使用循环链表模拟人围城一圈,每一个结点代表一个人.链表是一个有序链表,链表结点数据域是一个整型,代表人的序号.出局等同于链表删除元素,每次出局后重 ...
- java中for循环和while循环,哪个更快?--一道面试题
for的 while的
- Event Loop
Event Loop 是 JavaScript 异步编程的核心思想,也是前端进阶必须跨越的一关.同时,它又是面试的必考点,特别是在 Promise 出现之后,各种各样的面试题层出不穷,花样百出.这篇文 ...
- js异步、事件循环(EventLoop)小结
单线程 众所周知,JS是单线程的语言,之所以是单线程,用一句烂大街的话就是,如果两个线程同时操作一个DOM节点,那么该以哪个为准呢,虽然多线程也有办法解决,但是js毕竟是浏览器脚本语言,不需要那么复杂 ...
随机推荐
- Python 批量翻译 使用有道api;
妹子是做翻译相关的,遇到个问题,要求得到句子中的所有单词的 音标; 有道翻译只能对单个单词翻译音标,不能对多个单词或者句子段落翻译音标; 手工一个一个翻的话那就要累死人了.....于是就让我写个翻译音 ...
- 小白也能看懂插件化DroidPlugin原理(一)-- 动态代理
前言:插件化在Android开发中的优点不言而喻,也有很多文章介绍插件化的优势,所以在此不再赘述.前一阵子在项目中用到 DroidPlugin 插件框架 ,近期准备投入生产环境时出现了一些小问题,所以 ...
- documentsUI源码分析
documentsUI源码分析 本文基于Android 6.0的源码,来分析documentsUI模块. 原本基于7.1源码看了两天,但是Android 7.1与6.0中documentsUI模块差异 ...
- Linux项目自动部署
场景:linux中自动部署项目在工作中经常遇到,快速高效的部署项目能够大幅提高工作效率.现在将项目部署的过程记录下来,以供参考,其中用到的知识点现在还有很多不很清楚,后面要好好琢磨琢磨! 1 项目部署 ...
- 【JAVASCRIPT】React学习- 与 flux 结合使用
摘要 react 学习包括几个部分: 文本渲染 JSX 语法 组件化思想 数据流 与flux 结合使用
- 【css】圆角 +文本阴影
1. css3 圆角 http://www.cnblogs.com/lhb25/archive/2013/01/30/css3-border-radius.html 2. text shawdow ...
- VMware workstation批量创建虚拟机和自动化安装操作系统(一)
一. 简述 作为从事IT行业运维工作的Linuxer,大多情况下需要在测试环境中部署业务系统并进行测试,在没有足够的计算存储网络条件下,使用虚拟机进行虚拟集群的创建和使用,是一种不错的学习和实践方式. ...
- 倒水问题(Fill,UVA 10603) lrj白书 p202
看着lrj的代码自己敲了一遍,还没调试成功.... 有时间再进行完善 /* 状态start到各个状态u1,u2,u3..... 的倒水量分别为u1.dist,u2.dist,u3.dist.... * ...
- 2.跑nodejs文件
1.安装nodejs的环境,这里不介绍了. app.js 'use strict'; //process.argv 接受变量 var command = process.argv[2]; var a ...
- Ext js Grid
Ext.onReady(function () { var proxy = new Ext.data.HttpProxy({ ur ...