关于setTimeout的一些习题
1. setTimeout参数详解
setTimeout(func|code, delay) // 第一个参数是函数执行体,后面一个是延时时间
但是setTimeOut还可以在后面增加多个参数,比如:
setTimeout(func|code, delay,a,b,c ..) // 第一个参数是执行体,第二个参数是延时时间,后面的参数可以作为参数传入第一个函数执行体内作为参数使用
setTimeOut的参数:
第一个参数是执行体,第二个参数是延时时间,
后面的参数可以作为参数传入第一个函数执行体内作为参数使用。
比如:
for (var b = 0; b < 4; b++) {
setTimeout(function(c) {
console.log( c);
}, 1000, b);
}
console.log(c);
//打印出来
4,0,1,2,3
这是因为整个for循环是几乎是一瞬间执行完毕,相当于同一时间设置了四个定时器,每个定时器都是延时1s以后执行,并且每一个定时器都保存了创建它的时候b的值,所以,最后是先打印出4,然后过一秒几乎同时打印出0,1,2,3.
2. 设计一个循环定时器,每隔一秒打印一次
for(let i=0;i<5;i++){
setTimeout(()=>{
console.log(i)
},1000*i)
}//打印结果 依次打印0,1,2,3,4 时间间隔为1s
但是将let改成var之后
for(var i=0;i<5;i++){
setTimeout(function(){
console.log(i)
},i*1000)
}
//打印结果 依次打印5,5,5,5,5 时间间隔为1s
总结:setTimeout的i值,是一个全局变量i(可以跨块级作用域,for是块级作用域),每次循环这个值都会改变,指向的是最外层也就是全局的i值,当1秒后打印的时候,i的值已经变成了5,所以打印出来的是5个5。let是块级作用域内有效,setTimeout的i值指向的是每个循环体中的i值。
专业一点的解释:
- var: 由于
JavaScript是单线程的,按顺序执行,setTimeout是异步函数,它会将内部函数放到任务队列中,而此时会先将循环执行完毕再执行内部函数,因此当执行内部函数时i已经等于5了,所以最终会输出5个5。 - let:
let块级作用域,for循环外无法获取i,因为for循环头部的let不仅将i绑定到for循环中,事实上它将其重新绑定到循环体的每一次迭代中,确保上一次迭代结束的值重新被赋值。setTimeout里面的function()属于一个新的域,通过var定义的变量是无法传入到这个函数执行域中的,通过使用let来声明块变量能作用于这个块,所以function就能使用i这个变量了。
使用闭包解决:
for (var i = 0; i < 5; i++) {
(function (i) {
setTimeout(function() {
console.log(i);
}, 1000*i);
})(i)
}//打印结果 依次打印0,1,2,3,4 时间间隔为1s
通过闭包,将i的变量驻留在内存中,当console时,引用的是外部匿名函数的变量值i,i的值是根据循环来的。
3. 综合运用习题
let obj = {
a: 10,
b: function () {
console.log('普通函数', this.a)
}
}
setTimeout(obj.b(), 5000); //10
setTimeout(obj.b, 1000); //undefined
这里第一个setTimeout虽然设置了间隔时间,但是内部的函数是对obj.b方法的立即执行(从字面意思理解:立即函数就是立即执行无需等待回调,代码加载就立即执行)。所以会立马打印出: 普通函数 10。
第二个setTimeout可以看成是:
setTimeout(function() {
console.log('普通函数', this.a)
},1000)
正常的间隔一秒打印 普通函数 undefined, 定时器this是window,window没有a属性。
涉及知识点: 立即执行函数、setTimeout、this指向


