JS防抖和节流
前言
在网页实际运行的某些场景下,有些事件会不间断的被触发,如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 请求,而不是在用户停下滚动页面操作时才去请求数据。这样的场景,就适合用节流技术来实现。
参考文章:https://bbs.huaweicloud.com/blogs/297720?utm_source=cnblog&utm_medium=bbs-ex&utm_campaign=other&utm_content=content