for(var i=1;i<=5;i++){
setTimeout(function timer(){
console.log(i);
},i*1000);
}
for(var i=1;i<=5;i++){
setTimeout(function timer(){
console.log(i);
},i*1000);
}
这样写就好了,
for(var i=1;i<=5;i++){
(function(i){
setTimeout(function timer(){
console.log(i);
},i*1000)})(i);
}
把setTimeout立即执行。 我觉得应该是for循环执行之后才执行的timer函数
还有js没有块作用域,是函数作用域
改成这个样子会好懂一点;只是举个例子:
var tasks = [];
for(var i=1;i<=5;i++){
//(function(i){
// setTimeout(function timer(){
// console.log(i);
// },i*1000);
tasks.push(function timer(){console.log(i);});
}
//循环结束之后,依次执行推入tasks的timer函数
tasks.forEach(function(timer){
timer();//你猜是几呢?
})
一句话:因为那五个setTimeout的回调函数是在for循环完成后被调用的,那时候i已经是6了。
更详细一点:for循环是在当前一轮的事件循环进行的,5个回调函数的执行是在未来轮的事件循环进行的。
PS:异步经常会导致代码执行顺序与代码物理位置的先后循序不一致。
不论是node环境还是浏览器下,执行js的代码只有一个线程(所谓单线程),还有个任务队列的线程(不过这是浏览器或者v8提供的,并不执行代码);
setTimeout会把代码放在任务队列尾部;
不管在哪儿,都是先执行完所有js主线程的代码(即同步代码),然后再把任务队列里的代码往js主线程上丢,这也是setTimeout的原理;
所以,当你setTimeout有延迟,首先要等所有同步代码执行完,然后任务队列之前的代码执行,再是等待的时间,之后才会丢到主线程;
你这里,for是主线程的代码,先执行5次setTimeout把setTimeout里的匿名函数代码(即回调)放到任务队列,这样你任务队列就有5个待执行的任务;然后for执行完 i 此时就是6了;接着任务队列的5个代码进入主线程,开始执行。你得知道此时i已经是6了啊,所以就输出了5个6.
这是异步机制,js只有一个线程,setTimeout会在线程内的js执行完成之后,才会执行里面的代码。
for(var i=1;i<=5;i++){
setTimeout(function timer(){
console.log(i);
},i*1000);
}
for循环里面每一次都会执行setTimeout,因为异步机制,所以会先执行完for循环,每一个对应的setTimeout被压在队列之后。所以,for循环执行后i=6,之后setTimeout输出i。
需要注意的是,设定setTimeout(foo,time)第二个参数time的也不是说经过了time毫秒,函数就一定执行,如果线程里的js没执行完,还是需要等待。
13 回答12.8k 阅读
7 回答1.9k 阅读
3 回答1.1k 阅读✓ 已解决
2 回答1.2k 阅读✓ 已解决
6 回答872 阅读✓ 已解决
6 回答1k 阅读
2 回答1.3k 阅读✓ 已解决
首先js没有严格意义的块作用域,块作用域可以用一个立即调用的函数来模拟
然后你的这段代码和块作用域无关,这是一个js闭包的问题,所谓闭包就是可以访问另一个函数作用域中变量的函数,所以这里的setTimeout是一个闭包,它在for语句所在的作用域被调用,在它内部可以访问for语句中定义的(但不是在它自己作用域内)变量i,这是因为闭包保存了包含函数的整一个变量对象
所以,i从1到5,循环体执行5次,当最后因为i变为6跳出循环时,因为闭包引用的是外部作用域的整一个变量对象,也就引用了外部的这个i,所以每次输出的这个i就是外部的i,就是6
于是就是5次6