如果实现了dom拖拽功能,但是在绑定拖拽事件的时候发现每当元素稍微移动一点便触发了大量的回调函数,导致浏览器直接卡死,这个时候怎么办?
如果给一个按钮绑定了表单提交的post事件,但是用户有些时候在网络情况极差的情况下多次点击按钮造成表单重复提交,如何防止多次提交的发生?
为了应对如上场景,便出现了函数防抖和函数节流两个概念,总的来说:这两个方法是在时间轴上控制函数的执行次数。
函数防抖(debounce)
在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。这可以使用在一些点击请求的事件上,避免因为用户的多次点击向后端发送多次请求。
- 给按钮加函数防抖防止表单多次提交。
- 对于输入框连续输入进行AJAX验证时,用函数防抖能有效减少请求次数。
- 判断scroll是否滑到底部,滚动事件+函数防抖
函数节流(throttle)
规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。节流可以使用在 scroll 函数的事件监听上,通过事件节流来降低事件调用的频率。
- 游戏中的刷新率
- DOM元素拖拽
- Canvas画笔功能
共同点:都限制函数的执行频次,达到优化的目的
不同点:
- 事件节流
处理方法是只允许一个函数在 x 毫秒内执行一次,跟 防抖 主要的不同在于,节流 保证 x 毫秒内至少执行一次。 - 事件防抖
处理方法是把触发非常频繁的事件(比如key event ,resize)合并成一次执行
事件节流像是班车:固定在一段时间后执行,不会等乘客(事件),到点发车
事件防抖像是黑车:说是再等五分钟就走,但是如果又有人上车了(在5分钟内再次触发事件),那你又得等5分钟才能再出发(重新计时)
函数防抖的实现
function debounce(fn, wait) {
var timer = null;
return function() {
var context = this,
args = arguments;
// 如果此时存在定时器的话,则取消之前的定时器重新记时
if (timer) {
clearTimeout(timer);
timer = null;
}
// 设置定时器,使事件间隔指定事件后执行
timer = setTimeout(() => {
fn.apply(context, args);
}, wait);
};
}
函数节流的实现
function throttle(fn, delay) {
var preTime = Date.now();
return function() {
var context = this,
args = arguments,
nowTime = Date.now();
// 如果两次时间间隔超过了指定时间,则执行函数。
if (nowTime - preTime >= delay) {
preTime = Date.now();
return fn.apply(context, args);
}
};
}