前端开发中,我们的一些事件的响应比较慢或者需要请求接口完成的,我们不希望这些事件频繁执行,比如说需要对input输入的数据保存,监听keyup事件,如果每次键盘输入就执行保存请求,那样可能会产生很多频繁的请求,针对这种连续触发的高频率事件,函数防抖和函数节流给出了两种解决方法
防抖(debounce)
去抖动,方法是在函数触发时,设定一个周期延迟执行函数,若在周期内函数再次执行、则刷新延迟时间,直到最后执行函数,这里函数收集到的结果是最后一次操作的结果
简单的实现
var timer; // 定时器
function change (e) {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function () {
console.log(e.target.value);
timer = void 0;
}, 1000)
}
document.querySelector("#test").addEventListener('keyup', change);
这里监听input的keyup事件,change方法执行的时候会首先判断定时器是否存在、如果存在则clear掉,如果不则新建一个定时器延迟1s执行
封装
上面这样实现没毛病,但是却有一个问题,没有复用性,现在我来把他封装成一个公共的方法
function keyup (e) {
console.log(e.target.value);
}
function debounce (method, delay) {
var timer = void 0;
return function () {
var self = this;
var args = arguments;
timer && clearTimeout(timer);
timer = setTimeout(function () {
method.apply(self, args)
timer = void 0;
}, delay)
}
}
document.querySelector("#test").addEventListener('keyup', debounce(keyup, 1000));
节流(throttling)
节流的概念是设定一个周期,周期内只执行一次,若有新的事件触发则不执行,周期结束后又有新的事件触发开始新的周期
实现
比如说我们监听onscroll判断获取当前的scrollTop、可以用到节流
var start, timer, wait = 200
function scroll() {
var self = this;
var args = arguments;
if (!start) {
//第一次触发,设置start时间
start = Date.now()
}
// 当前时间减去开始时间大于等于设定的周期则执行并且初始化start、timer
if (Date.now() - start >= wait) {
console.log('触发了')
start = timer = void 0;
} else {
timer && clearTimeout(timer)
timer = setTimeout(function () {
scroll.apply(self, arguments)
},wait)
}
}
document.addEventListener('scroll', scroll)
封装
function throttling (method, wait) {
var start, timer
return function run () {
var self = this;
var args = arguments;
if (!start) {
start = Date.now();
}
if (Date.now() - start >= wait) {
method.apply(self, args)
start = timer = void 0
} else {
timer && clearTimeout(timer)
timer = setTimeout(function () {
run.apply(self, args)
}, wait)
}
}
}
function scroll() {
console.log('触发了')
}
document.addEventListener('scroll', throttling(scroll, 200))
这里需要注意的就是参数和this指向的问题
总结
节流和防抖各有特点,如果是需要一连串频繁的事件只执行最后一次选择防抖、其它可以选择节流,具体业务具体分析
若文中有错误的地方望多多指正