是什么?
防抖:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。
节流:每隔一段时间,只执行一次函数。
防抖和节流图解:demo.nimius.net/debounce_th…
为什么?
为了解决前端工作中一些场景性能问题:在浏览器中,有一些事件(滚动,窗口大小变化)触发的频率过高,或者有些用户可能疯狂点击某个按钮或者在输入框里面疯狂输入(可能是网太慢很着急或者一些其他原因);如果每一次变化、点击和输入都触发事件,就会不停占用资源可能到溢栈等一些缺陷的常胜,然后导致用户体验变差或者功能直接死掉的情况。就出现了防抖和节流的方式来解决这些问题。
防抖和节流通过控制这些事件在一段时间内调用的频率,从而使资源合理分配和少牺牲用户体验。
如何实现?
防抖 - debounce
定义:防抖是在事件触发 n 秒后在执行回调函数,如果在这 n 秒内又被触发,重新计时。
触发 n 秒后,首先需要一个计时器,用来让 n 秒后触发执行;
执行回调函数,这时候需要把传入的参数给到回调函数;
执行完回调函数,需要返回回调函数的结果;
防抖是给一个函数增加一个功能,防抖功能,当目标函数需要的时候运行触发。
所以防抖返回的是一个增加了新功能的函数。
// 基础版本
function debounce(fn, delay) {
let timer; // 计时器
const debounceFn = function () { // 加上了功能的函数
const context = this; // 存储当前 this
if (timer) clearTimeout(timer); // n 秒内重新触发,清除计时器归零
timer = setTimeout(() => { // n 秒后执行
return fn.apply(context, arguments); // 执行回调函数
}, delay)
}
return debounceFn; // 返回加了功能的函数
}
有了基础版本,可以在上面进行优化和提供更多功能,如:
- 增加取消功能
// 取消功能,我们返回的是函数变量,怎么办?那就在返回的函数变量上新增一个方法取消
function debounce(fn, delay) {
let timer;
const debounceFn = function () {
const context = this;
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
return fn.apply(context, arguments);
}, delay);
}
// 新增的取消在这里
debounceFn.cancel = function () {
clearInterval(timer);
timer = null;
}
return debounceFn;
}
还可以增加更多功能,如
- 增加立即调用功能
flush - ...
更多可以参考 lodash 的防抖功能,lodash 的实现
节流 - throttle
定义:每隔一段时间,只执行一次函数。
重要的是在一个事件频繁触发的情况下,固定一段事件间隔执行一次函数;
给一个函数事件新增一个节流的功能,我们的函数签名输入是回调函数和间隔时间,输出为给回调函数赋予了节流功能的新函数;
实现思路:当触发事件的时候,我们设置一个定时器,再触发事件的时候,如果定时器存在,就不执行,直到定时器执行,然后执行函数,清空定时器,这样就可以设置下个定时器。
function throttle(fn, interval) {
let timer;
const throttleFn = function() {
const context = this;
let result;
if (!timer) {
timer = setTimeout(() => {
timer = null;
result = fn.apply(context, arguments)
})
}
return result;
}
}
可增加更多的功能:
- 取消功能,同防抖一样;
- ...
更多可以参考 lodash 的节流功能,lodash 的节流实现
使用场景
不用硬记使用场景,理解了概念和几个实例场景之后会在脑子留下了一个钩子,当自己重新进入相应的场景会唤起自己的大脑,会想到是不是可以做一个这样的优化。以下为一些常见的使用实例场景:
- 防抖
- Input输入的搜索或验证(输入后需要进行后台搜索或验证)
- 节流
- DOM 元素的拖拽功能实现(mousemove)
- 射击游戏的 mousedown/keydown 事件(单位时间只能发射一颗子弹)
- 计算鼠标移动的距离(mousemove)
- Canvas 模拟画板功能(mousemove)
- 搜索联想(keyup)
注意
- 使用防抖的时候避免绑定错误
// 错误
$(window).on('scroll', function() {
_.debounce(doSomething, 300);
});
// 正确
$(window).on('scroll', _.debounce(doSomething, 200));