关于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
指向