js中的定时器

你真的了解setTimeout和setInterval吗?

1.基础用法:setTimeout('console.log(2)',1000);会在一秒后打印2,那下面这段代码呢?

1
2
3
console.log(1);
setTimeout('console.log(2)',1000);
console.log(3);

输出是132,因为js事件循环机制,setTimeout这类属于宏任务,promise这类属于微任务,主线任务结束后才会清空微任务队列,最后清空宏任务队列,所以setTimeout会在最后才执行。
2.第一个参数为什么是字符串?
因为js引擎会用eval函数将字符串转为代码,但是如果是函数名,则可以不用字符串(实际上都是这么做的),如:

1
2
3
4
5
6
function func(){
console.log(2);
}
setTimeout(func,1000);
// 或者
setTimeout(function (){console.log(2)},1000);

3.this
如果被setTimeout推迟执行的回调函数是某个对象的方法,那么该方法中的this关键字将指向全局环境,而不是定义时所在的那个对象,如:

1
2
3
4
5
6
7
8
9
var x = 1;

var o = {
x: 2,
y: function(){
console.log(this.x);
}
};
setTimeout(o.y,1000);// 1

解决办法一:用匿名函数
解决办法二:用bind绑定
3.运行机制
首先要了解js的事件循环(不知道的快去查),第一轮循环后检查时间到了没,到了就执行,没到等下一轮,所以是没有办法保证定时器的时间准确,如果后面立即运行的任务(当前脚本的同步任务))非常耗时,过了100毫秒还无法结束,那么被推迟运行的someTask就只有等着,等到前面的veryLongTask运行结束,才轮到它执行。
4. setTimeout(func,0)

1
2
3
4
setTimeout(function () {
func1();
}, 0)
func2();

即使这样也是func2线运行,因为上面提到了,最早也是第一轮循环后才会执行setTimeout函数,延时值为0的意思是,尽早执行,但是实际上最快也要4毫秒,也就是说0毫秒也会变成4毫秒,如果延时写的很大溢出了,会和0的效果相同。
那0有什么用呢?
可以调整事件发生的顺序,比如父子元素均有点击的事件回调函数,因为冒泡的机制,子元素函数会先执行,此时setTimeout(func,0)就起作用了,想让父元素点击回调函数先执行,只需要把子元素回填函数放入即可。
5.防抖

1
2
3
4
5
6
7
8
9
10
11
function debounce(fn, delay){
var timer = null; // 声明计时器
return function(){
var context = this;
var args = arguments;
clearTimeout(timer);
timer = setTimeout(function(){
fn.apply(context, args);
}, delay);
};
}
上一篇

三列布局