前言

 在网页实际运行的某些场景下,有些事件会不间断的被触发,如scroll事件,而不像我们想象中的,滚动一次触发一次,稍微滚动一下就会触发n多次scroll事件。如下:



window.onscroll = function (){
console.log(123);
} //监听滚动条滑动


 我只是轻微的滚动了一下滚动条就触发了这么多次的scroll事件,这种情况下,由于过于频繁地DOM操作和资源加载,严重影响了网页性能,甚至会造成浏览器崩溃。

 此时,我们可以采用 debounce(防抖)和 throttle(节流)的方式来减少调用频率,同时又不影响实际效果。

 

  防抖

 防抖通过设置setTimeout定时器的方式延迟执行,当快速多次点击的时候,每一次都会重置定时器,只有你一段时间都不点击时定时器才能到达条件并执行事件函数。即如果触发事件后在 n 秒内又触发了事件,则会重新计算函数延执行时间。

 如下,我们模拟一个表单提交的例子,多次快速点击提交后只会执行一次:



<input type="submit" id="btn" value="提交">
<script>
var btn = document.getElementById('btn');
// 点击后触发debounce()函数,第一个参数为真实要执行的函数,第二个参数为定时器延迟时间
btn.addEventListener('click',debounce(submit,1000));
//真实要执行的函数
function submit(e){
console.log("提交成功!");
console.log(this)
console.log(e);
}
//防抖函数
function debounce(fn,delay){
//设置time为定时器
var time = null;
//闭包原理,返回一个函数
return function (e){
//如果定时器存在则清空定时器
if(time){
clearTimeout(time);
}
//设置定时器,规定时间后执行真实要执行的函数
time = setTimeout(() => {//此箭头函数里的this指向btn这个按钮
fn.call(this,arguments);//改变真实要执行函数的this指向,原submit函数里面的this指向window
},delay);
}
}
</script>


 运行后,狂点提交按钮停下来后只会执行一次,而不会出现多次点击而多次提交。对于代码中的闭包还有箭头函数的this指向问题不清楚的,可以翻看我的这两篇:​​闭包​​、​​箭头函数​​。

 

  节流

 节流其实就很好理解了,减少一段时间的触发频率。简单来说,就是你一直狂点不停的话,它会每隔一定时间就执行一次。它与防抖最大的区别就是,无论事件触发多么频繁,都可以保证在规定时间内可以执行一次执行函数。下面利用计算时间戳实现:



<input type="submit" id="btn" value="提交">
<script>
var btn = document.getElementById('btn');
// 点击后触发debounce()函数,第一个参数为真实要执行的函数,第二个参数为定时器延迟时间
btn.addEventListener('click',throttle(submit,500));
//真实要执行的函数
function submit(e){
console.log("提交成功!");
console.log(this)
// console.log(e);
}
//节流函数
function throttle(fn,delay){
//bef为上一次执行时间,初始值为0
var bef = 0;
return function (e){
//获取当前时间戳
var now = new Date().getTime();
//如果当前时间减去上次时间大于限制时间时才执行
if(now - bef > delay){
console.log(this);
fn.call(this,arguments);
bef = now;
}
}
}
</script>


 运行后,狂点不停的话,每隔500毫秒才执行一次。

 也可以用定时器实现节流,如下:



<input type="submit" id="btn" value="提交">
<script>
var btn = document.getElementById('btn');
// 点击后触发debounce()函数,第一个参数为真实要执行的函数,第二个参数为定时器延迟时间
btn.addEventListener('click',throttle(submit,500));
//真实要执行的函数
function submit(e){
console.log("提交成功!");
// console.log(this)
console.log(e);
}
//节流函数
function throttle(fn,delay){
var flag = true;
return function (e){
if(flag){
setTimeout(() => {
//到规定时间后执行函数,同时flag=true
fn(this,arguments);
flag = true;
},delay);
}
//防止一直执行
flag = false;
};
}

</script>


 

  总结

 最后我们也明白了防抖和节流的主要区别,那么他们各自适应的场景又有哪些呢?一般当我们提交表单时使用防抖,但在页面的无限加载场景下,我们需要用户在滚动页面时,每隔一段时间发一次 ajax 请求,而不是在用户停下滚动页面操作时才去请求数据。这样的场景,就适合用节流技术来实现。