防抖与节流

第一次接触防抖与节流的概念是在一次面试中,但是当时并没有觉得防抖与节流很重要,知道后来学习vue做项目的时候才明白,防抖与节流能够有效地节省浏览器资源,因此更加深入地学习了防抖与节流的知识

函数防抖

当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。

在一个页面中我们会发现点击一次↓键会触发很多次scroll事件,如果我们在scroll事件中添加对是否显示回到顶部按钮条件的判定,那么这时会消耗大量的浏览器资源,做很多无用功,此时我们就需要防抖函数的出场了

我们提出这样一种设想: 当我们触发第一次scroll事件时启动一个延时函数,如果延时时间内再次触发了scroll事件,那么此时我们重新开始计时,直到延时时间内没有再次触发该事件然后再执行事件对应的函数

1
2
3
4
5
6
7
function debounce(fn, deplay = 1000) {
let timer = null;
return () => {
if(timer) clearTimeout(timer);
timer = setTimeout(fn, deplay)
}
}

对于setTimeout(),如果我们没有给deplay进行赋值,将会赋予其默认值1000ms
简单修改之后,代码如下:

1
2
3
4
5
6
7
8
9
function debounce(func, deplay) {
let timer = null;
return function(...args) {
if(timer) clearTimeout(timer)
timer = setTimeout(() => {
func.apply(this, args)
}, deplay)
}
}

适用场景:

  • 表单提交场景:可以防止多次点击提交按钮,只执行最后一次的提交操作
  • 服务端验证场景:如联想搜索,表单验证等,只执行最后一次输入后触发得到事件

节流

节流就是指连续触发事件,但是在n秒规定时间内只会执行一次函数

节流的使用虽然带来了很多便利,但是依旧有一些情况是防抖处理不了的,例如:当一个页面在浏览器中被打开,一个用户不断上下拖动进度条,此时我们

将永远不会执行scroll事件中的处理函数,因此我们需要使用另外一种策略,当用户第一次触发事件后一段时间内再次触发会失效,这也就是节流

1
2
3
4
5
6
7
8
9
10
11
const throttle = (func, deplay = 500) => {
let flag = true;
return (...args) => {
if(!flag) return;
flag = false;
setTimeout(() => {
func.apply(this, args);
flag =true
}, deplay)
}
}

代码解析: 当我们第一次触发事件时,flag=true,此时将flag设定为false即锁定函数,开始计时执行处理函数,计时期间内再次触发事件时,flag = false直接跳过所有步骤使本次触发失效

通过阅读文章我还找到了另外一种方法,使使用时间戳进行判定的

1
2
3
4
5
6
7
8
9
10
11
const throttle = (func, deplay = 500) => {
let preTime = 0;
return (...args) => {
let now = new Date();
let context = this;
if(now - preTime > deplay) {
func.apply(context, args);
preTime = now
}
}
}

代码解析: 其实这个时间戳的版本也很好理解,每次触发事件获取一次现在的时间,如果与初次触发时间的差大于等待时间,那么执行函数,如果小于或等于即不执行

区别: 以上两种方法的区别就是,使用定时器会在延时以后执行函数,而时间戳方式是立即执行

试用场景:

  • 拖拽场景:固定时间内只执行一次,防止超高频次触发位置变动
  • 缩放场景:监控浏览器resize
  • 动画场景:避免短时间内多次触发动画引起性能问题
  • 部分后台系统表单中的快捷操作按钮