窗口的
resize
、scroll
,输入框内容校验等操作时,如果这些操作处理函数较为复杂或页面频繁重渲染等操作时,如果事件触发的频率无限制,会加重浏览器的负担,导致用户体验非常糟糕。此时我们可以采用debounce(防抖)和throttle(节流)的方式来减少触发的频率,同时又不影响实际效果。
节流(throttle)
- 概念理解:固定函数执行的速率。
- 使用场景:DOM
- 事件绑定,短时间内触发多次绑定事件造成性能问题,如 onresize,onscroll等,使用节流函数可确保在指定时间内只触发一次。
简单实现:
let throttle = (func, wait) => {
let context, args;
let previous = 0;
return function () {
let now = +new Date();
context = this;
args = arguments;
if (now - previous > wait) {
func.apply(context, args);
previous = now;
}
};
};
上图中绿色块表示触发一次事件,持续触发事件时,throttle会合并一定时间内的事件,并在该时间结束时真正去触发一次事件~,下面是另一种实现
function throttle(fn, delay) {
var ctx;
var args;
// 记录上次触发事件
var previous = Date.now();
var later = function () {
fn.apply(ctx, args);
};
return function () {
ctx = this;
args = arguments;
var now = Date.now();
// 本次事件触发与上一次的时间比较
var diff = now - previous - delay;
// 如果隔间时间超过设定时间,即再次设置事件触发的定时器
if (diff >= 0) {
// 更新最近事件触发的时间
previous = now;
setTimeout(later, delay);
}
};
}
效果图:
防抖(debounce)
概念理解:强制一个函数在某个连续时间段内只执行一次,哪怕它本来会被调用多次。
使用场景:
- 表单远程搜索,键盘多次输入取最后一次输入值作为查询关键词
- 下拉菜单的延时消失。
简单实现:
let debounce = (func, wait) => {
let timeout;
return function () {
let context = this;
let args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function () {
func.apply(context, args)
}, wait);
};
};
从上图可以看到,当持续触发事件时,debounce会合并事件且不会去触发事件,当一定时间内没有触发再这个事件时,才真正去触发事件~下面是另一种实现
function debounce(fn, delay) {
var ctx;
var args;
var timer = null;
var later = function () {
fn.apply(ctx, args);
// 当事件真正执行后,清空定时器
timer = null;
};
return function () {
ctx = this;
args = arguments;
// 当持续触发事件时,若发现事件触发的定时器已设置时,则清除之前的定时器
if (timer) {
clearTimeout(timer);
timer = null;
}
// 重新设置事件触发的定时器
timer = setTimeout(later, delay);
};
}
效果图: