在上一篇文章中提到了防抖與節流函數在js中的實際應用,這個直接和用戶體驗以及瀏覽器加載,服務器負載相關,因此防抖與節流對於前端開發人員來講是必需要掌握的一種技術。下面就根據一個小案例來具體體現防抖與節流的做用。前端
首先,沒有進行防抖與節流處理的鼠標移動事件:git
1 var num = 1; 2 var oWrap = document.getElementById('wrap'); 3 function count (){ 4 oWrap.innerHTML = num++; 5 } 6 oWrap.onmousemove = count;
上面代碼中,給div綁定了鼠標移動事件,當鼠標在盒子內頻繁移動時,事件處理函數也在持續的被調用,這樣會對瀏覽器形成沒必要要的負載。github
防抖函數(debounce)瀏覽器
防抖函數(debounce):指事件在觸發後指定時間內只執行一次,若在指定時間內又觸發了事件,則從新計算函數執行時間,防抖函數分爲當即執行版和非當即執行版服務器
非當即執行版:app
1 function debounce(fn,wait){ 2 var timeout; 3 return function(){ 4 var context = this; 5 var args = arguments; 6 if(timeout){ 7 clearTimeout(timeout); 8 } 9 timeout = setTimeout(()=>{ 10 fn.apply(context,args) 11 },wait); 12 } 13 } 14 oWrap.onmousemove = debounce(count,1000);
上述非當即執行防抖函數結果顯示爲鼠標在盒子內頻繁移動(此處gif看不出效果,不過真實在移動)時,函數不會當即執行,在1000ms事後會執行一次事件函數,當我在觸發事件後在1s內再次觸發事件會從新計算執行時間並在結束事件後執行函數函數
當即執行版:this
1 function debounce(fn,wait){ 2 var timeout; 3 return function(){ 4 var context = this; 5 var args = arguments; 6 if(timeout){ 7 clearTimeout(timeout); 8 } 9 var callNow = !timeout; 10 timeout = setTimeout(()=>{ 11 timeout = null; 12 },wait) 13 if(callNow){ 14 fn.apply(context,args); 15 } 16 } 17 } 18 oWrap.onmousemove = debounce(count,1000);
當即執行版防抖函數是在觸發事件後當即執行,並在1000ms後事件再次觸發時調用執行函數,若在延遲時間內再次觸發會從新計算執行時間spa
結合版:3d
1 function debounce(fn,wait,immediate){ 2 var timeout; 3 return function (){ 4 var context = this; 5 var args = arguments; 6 if(timeout){ 7 clearTimeout(timeout); 8 } 9 if(immediate){ 10 // immediate判斷函數爲當即執行仍是非當即執行(true爲當即執行,false爲非當即執行) 11 var callNow = !timeout; 12 timeout = setTimeout(()=>{ 13 timeout = null; 14 },wait) 15 if(callNow){ 16 fn.apply(context,args); 17 } 18 else{ 19 timeout = setTimeout(function(){ 20 fn.apply(context,args); 21 },wait); 22 } 23 } 24 } 25 } 26 oWrap.onmousemove = debounce(count,1000,1)
兩者結合版是在事件觸發後會當即執行一次函數並在1000ms後再次執行一次函數(注:存在bug,哈哈!!!當觸發事件後在延遲時間內再次觸發或者頻繁觸發事件依舊會執行屢次函數)
節流函數(throttle)
節流函數(throttle):指事件在頻繁觸發後在指定時間內觸發一次,節流會稀釋函數的執行頻率,對於節流,存在時間戳和定時器兩種方式
時間戳版:
1 function throttle(func, wait) { 2 var previous = 0; 3 return function () { 4 var now = Date.now(); 5 var context = this; 6 var args = arguments; 7 if (now - previous > wait) { 8 func.apply(context, args) 9 previous = now; 10 } 11 } 12 } 13 oWrap.onmousemove = throttle(count,1000)
能夠看出,鼠標在盒子內頻繁移動時,函數會當即執行,且每1s執行一次
定時器版:
1 function throttle(fn,wait){ 2 var timeout; 3 return function(){ 4 var context = this; 5 var args = arguments; 6 if(!timeout){ 7 timeout = setTimeout(()=>{ 8 timeout = null; 9 fn.apply(context,args) 10 },wait) 11 } 12 } 13 } 14 oWrap.onmousemove = throttle(count,1000)
定時器版節流函數在事件觸發後不會當即執行,會在1s後執行一次,當持續觸發時會每隔1s執行一次,在中止觸發事件後,函數會再執行一次
結合版:
1 function throttle(fn, wait, type) {
// type判斷節流函數方式,1爲時間戳版,2爲定時器版 2 if (type === 1) { 3 var previous = 0; 4 } else if (type === 2) { 5 var timeout; 6 } 7 return function () { 8 var context = this; 9 var args = arguments; 10 if (type === 1) { 11 var now = Date.now(); 12 13 if (now - previous > wait) { 14 fn.apply(context, args); 15 previous = now; 16 } 17 } else if (type === 2) { 18 if (!timeout) { 19 timeout = setTimeout(() => { 20 timeout = null; 21 fn.apply(context, args) 22 }, wait) 23 } 24 } 25 } 26 } 27 oWrap.onmousemove = throttle(count,1000,2)
參考文章: