无论是防抖还是节流都是为了避免回调函数中的处理随着连续触发事件每次都执行
防抖和节流都是为了防止函数(事件)的连续触发(连续触发有很多种onscroll,onkeydown等键盘事件和onmousemove鼠标事件等)所造成的性能问题,其实类似于事件循环和虚拟dom,对于事件的业务代码我们都放在回调函数里,同时不可能每次改变我都需要立马给你同步,所以我们需要控制回调函数执行的频率(就和一些事一样太频繁了也不好),连续触发的会造成函数的连续执行这样肯定会在性能上有影响
防抖
总体来说防抖同样没有破坏事件控制业务代码的行为,在防抖函数找那个控制业务代码频率的仍然是事件本身,定时器只是辅助作用
如下:
// 计时器
var timer = false;
//
window.onscroll = function(){
clearTimeout(timer);
timer = setTimeout(function(){
console.log("防抖");
console.log(new Date());
},300);
};
连续触发的滚动事件:滚动事件的触发是连续的,同时事件函数的运行与触发滚动的频率是一致的。我们想要执行的业务代码在定时器函数中,正常一个函数运行的时候我们首先创建一个执行栈去存放同步代码,同时起一个任务队列(也就是异步代码),我们事件函数连续触发我们会被不断地将同步代码clearTimeout(timer)执行,这样我们异步队列挂起待执行的setTimeout的回调函数的执行条件是300ms,但是事件连续触发的频率远远小于300ms,导致clearTimeout(timer)的函数执行始终在任务队列中没有执行的时候就把定时器干掉,所有保证执行的先决条件就是滚动停止的时候即截止到最后一次没有被干掉的定时器触发了业务代码
节流
节流的思想其实相对于防抖来说他就是让回调函数的业务代码在指定的时间内只运行一次,此处指定的时间划重点,此时控制的主动权在定时器,定时器的业务代码保证在定时器的指定时间内只进行,当然这时需要用一个控制开关去控制代码运行,这时候就需要一个变量去起到借力打力的作用。
看下边的一个例子
var flag = true;
window.onscroll = function(){
if(flag) {
console.log("节流" + new Date());
flag = false;
}
setTimeout(function(){
flag = true;
},300);
};
在上面代码中我们看到定义了一个变量flag去作为控制开关,并且定义这个开关的位置并不是事件函数的本身的私有作用域。因为私有作用域中的变量很容易被本身的执行栈所控制导致变量被重复声明,导致本身对flag的改变都被开始的var所覆盖。联想到作用域链,我们设置这个开关我们只想用他的值去,而且去改变他的值时不需要对它重新声明,所以我们我们需要把它提升到比他高一级的作用域中,这样就不需要我们去重复声明。
运行过程:我们通过变量的去控制业务代码,当flag为true时我们才会执行业务代码,当我们的业务代码执行完成后,我们将开关赋值为false,这样事件触发时就不会执行业务代码,但我们把开关的开启放在定时器,没当定时器的时间到的时候我们都会去触发开关的flag的值为true,这样下一次事件触发时业务代码会再次执行