性能优化之 防抖与节流的原理
面试中经常遇到面试官问到,手写一个防抖或者节流吧。
这道题真是说容易也容易,说不容易也不容易的题。
如果你没有自己仔细的研究过,甚至连二者的差别都说不清楚 ,就更别提手写出代码来了。

那下面我们就一起来学习一下防抖和节流分别是什么功能,它们俩又是如何实现的呢。
节流
节流就是控制流量,在某段时间内,只执行一次。
防抖
某段时间内未结束操作,则重新计时,并在规定时间内,仅执行一次。
看起来感觉差不多,
比如浏览器的onscroll,oninput,resize,onkeyup等,如果用户一直操作滚动视窗、缩放浏览器大小,则会重新计算延时,若操作稳定了,则在约定时间后执行一次方法。

行吧。下面先实现一个简单版本的节流:
/** @func 要节流的函数 @wait 时间间隔 */ function throttle(func, wait){ let pre = 0;//记录上一次的时间 return function(){ let now = Date.now();//记录当前时间 if(now - pre > wait){ //如果时间差大于约定的时间间隔 func.apply(this, arguments); //则触发 pre = now; //同时将时间更新 } } }
参考代码:Easy Throttle
上面是利用时间戳的方式实现,有一个问题存在,第一次触发会执行func,但停止触发后,不会再执行一次func。
我们试着使用定时器来优化一下,并将停止触发后要不要再执行一次作为一个参数trailing传递。
function throttle(func, wait, options) { let args, context; let pre = 0; let timer; let later = function () { pre = Date.now(); func.apply(context, args); args = context = null; }; let throttled = function () { args = arguments; context = this; let now = Date.now(); let remaining = wait - (now - pre); if (remaining <= 0) { //第一次执行,如果已经存在,则清除 if (timer) { clearTimeout(timer); timer = null; } func.apply(context, args); pre = now; } else if (!timer && options.trailing !== false) { //默认会触发,最后一次应该触发。 timer = setTimeout(later, remaining); } }; return throttled; }
调用时:
let btn = document.getElementById("btn"); btn.addEventListener("click", throttle(logger, 1000, { trailing: true })); //1s内都算一次 function logger() { console.log("logger"); }
参考代码:停止触发后要不要再次执行
那么既然有限制停止触发后的再次执行,就有对于首次加载时需不需要立即执行的限制,这里我们用参数leading:false来表示一进来不要立即执行。
function throttle(func, wait, options){ let args,context; let pre = 0; let timer; let later = function(){ pre = options.leading === false ? 0:Date.now(); func.apply(context, args); args = context = null; } let throttled = function (){ args = arguments; context = this; let now = Date.now(); //一进来不要立刻执行 if(!pre && options.leading === false){ pre = now; } let remaining = wait - (now - pre); if(remaining <= 0){//第一次执行 if(timer){ clearTimeout(timer); timer = null; } func.apply(context, args); pre = now; }else if(!timer && options.trailing !== false){//默认会触发,最后一次应该触发。 timer = setTimeout(later, remaining); } } return throttled; }
调用时:
let btn = document.getElementById("btn"); btn.addEventListener("click", throttle(logger, 1000, { leading: false })); function logger() { console.log("logger"); }
参考代码:是否立即执行
防抖:
function debounce(func, wait, immediate){ let timer; return function(){ clearTimeout(timer); if(immediate){ let callNow =!timer; if(callNow) func.apply(this, arguments); } timer = setTimeout(()=>{ func.apply(this, arguments) timer = null; }, wait); } }
调用时:
let btn2 = document.getElementById('btn2'); btn2.addEventListener('click',debounce(logger,1000, true)); //停止 时才算一次 function logger(e){ console.log('logger2',e); }
参考代码:debounce
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号