javascript的防抖節(jié)流函數(shù)解析
防抖節(jié)流函數(shù)的解析
認識防抖和節(jié)流函數(shù)
防抖和節(jié)流的概念其實最早并不是出現(xiàn)在軟件工程中,防抖是出現(xiàn)在電子元件中,節(jié)流出現(xiàn)在流體流動中
- 而JavaScript是事件驅(qū)動的,大量的操作會觸發(fā)事件,加入到事件隊列中處理。
- 而對于某些頻繁的事件處理會造成性能的損耗,我們就可以通過防抖和節(jié)流來限制事件頻繁的發(fā)生;
防抖和節(jié)流函數(shù)目前已經(jīng)是前端實際開發(fā)中兩個非常重要的函數(shù),也是面試經(jīng)常被問到的面試題。
但是很多前端開發(fā)者面對這兩個功能,有點摸不著頭腦:
- 某些開發(fā)者根本無法區(qū)分防抖和節(jié)流有什么區(qū)別(面試經(jīng)常會被問到);
- 某些開發(fā)者可以區(qū)分,但是不知道如何應(yīng)用;
- 某些開發(fā)者會通過一些第三方庫來使用,但是不知道內(nèi)部原理,更不會編寫;
認識防抖debounce函數(shù)
我們用一副圖來理解一下它的過程:
- 當(dāng)事件觸發(fā)時,相應(yīng)的函數(shù)并不會立即觸發(fā),而是會等待一定的時間;
- 當(dāng)事件密集觸發(fā)時,函數(shù)的觸發(fā)會被頻繁的推遲;
- 只有等待了一段時間也沒有事件觸發(fā),才會真正的執(zhí)行響應(yīng)函數(shù);
防抖的應(yīng)用場景很多:
- 輸入框中頻繁的輸入內(nèi)容,搜索或者提交信息;
- 頻繁的點擊按鈕,觸發(fā)某個事件;
- 監(jiān)聽瀏覽器滾動事件,完成某些特定操作;
- 用戶縮放瀏覽器的resize事件;
防抖函數(shù)的案例
我們都遇到過這樣的場景,在某個搜索框中輸入自己想要搜索的內(nèi)容:
比如想要搜索一個MacBook:
- 當(dāng)我輸入m時,為了更好的用戶體驗,通常會出現(xiàn)對應(yīng)的聯(lián)想內(nèi)容,這些聯(lián)想內(nèi)容通常是保存在服務(wù)器的,所以需要一次網(wǎng)絡(luò)請求;
- 當(dāng)繼續(xù)輸入ma時,再次發(fā)送網(wǎng)絡(luò)請求;
- 那么macbook一共需要發(fā)送7次網(wǎng)絡(luò)請求;
- 這大大損耗我們整個系統(tǒng)的性能,無論是前端的事件處理,還是對于服務(wù)器的壓力;
但是我們需要這么多次的網(wǎng)絡(luò)請求嗎?
- 不需要,正確的做法應(yīng)該是在合適的情況下再發(fā)送網(wǎng)絡(luò)請求;
- 比如如果用戶快速的輸入一個macbook,那么只是發(fā)送一次網(wǎng)絡(luò)請求;
- 比如如果用戶是輸入一個m想了一會兒,這個時候m確實應(yīng)該發(fā)送一次網(wǎng)絡(luò)請求;
- 也就是我們應(yīng)該監(jiān)聽用戶在某個時間,比如500ms內(nèi),沒有再次觸發(fā)時間時,再發(fā)送網(wǎng)絡(luò)請求;
這就是防抖的操作:只有在某個時間內(nèi),沒有再次觸發(fā)某個函數(shù)時,才真正的調(diào)用這個函數(shù);
認識節(jié)流throttle函數(shù)
我們用一副圖來理解一下節(jié)流的過程
- 當(dāng)事件觸發(fā)時,會執(zhí)行這個事件的響應(yīng)函數(shù);
- 如果這個事件會被頻繁觸發(fā),那么節(jié)流函數(shù)會按照一定的頻率來執(zhí)行函數(shù);
- 不管在這個中間有多少次觸發(fā)這個事件,執(zhí)行函數(shù)的頻繁總是固定的;
節(jié)流的應(yīng)用場景:
- 監(jiān)聽頁面的滾動事件;
- 鼠標移動事件;
- 用戶頻繁點擊按鈕操作;
- 游戲中的一些設(shè)計;
節(jié)流函數(shù)的應(yīng)用場景
很多人都玩過類似于飛機大戰(zhàn)的游戲
在飛機大戰(zhàn)的游戲中,我們按下空格會發(fā)射一個子彈:
- 很多飛機大戰(zhàn)的游戲中會有這樣的設(shè)定,即使按下的頻率非常快,子彈也會保持一定的頻率來發(fā)射;
- 比如1秒鐘只能發(fā)射一次,即使用戶在這1秒鐘按下了10次,子彈會保持發(fā)射一顆的頻率來發(fā)射;
- 但是事件是觸發(fā)了10次的,響應(yīng)的函數(shù)只觸發(fā)了一次;
自定義防抖和節(jié)流函數(shù)
我們按照如下思路來實現(xiàn):
防抖基本功能實現(xiàn):可以實現(xiàn)防抖效果
- 優(yōu)化一:優(yōu)化參數(shù)和this指向
- 優(yōu)化二:優(yōu)化取消操作(增加取消功能)
- 優(yōu)化三:優(yōu)化立即執(zhí)行效果(第一次立即執(zhí)行)
- 優(yōu)化四:優(yōu)化返回值
function debounce(fn,delay,immediate=false,resultCallback){ let timer=null // console.log(this)//window // 定義控制立即執(zhí)行的變量,false表示沒有執(zhí)行過 let isInvoke=false // 真正的處理函數(shù) function _debounce(...args){ // 取消事件執(zhí)行操作 if(timer) clearTimeout(timer) // console.log(this)//element元素 if(immediate&&!isInvoke){ const result=fn.apply(this,args) resultCallback(result) isInvoke=true }else{ // 延遲執(zhí)行 timer=setTimeout(()=>{ const result=fn.apply(this,args) resultCallback(result) timer=null isInvoke=false },delay) } } // 封裝取消請求 _debounce.cancel=function(){ if(timer) clearTimeout(timer) timer=null isInvoke=false } return _debounce }
我們按照如下思路來實現(xiàn):
節(jié)流函數(shù)的基本實現(xiàn):可以實現(xiàn)節(jié)流效果
- 優(yōu)化一:節(jié)流最后一次也可以執(zhí)行
- 優(yōu)化二:優(yōu)化添加取消功能
- 優(yōu)化三:優(yōu)化返回值問題
function throttle(fn,interval,options={leading:true,trailing:false}){ let lastTime=0 const {leading,trailing,resultCallback}=options let timer=null function _throttle(...args){ const nowTime=new Date().getTime() // leading優(yōu)化 if(!leading&&!lastTime) lastTime=nowTime let remainTime=interval-(nowTime-lastTime) if(remainTime<=0){ if(timer){ clearTimeout(timer) timer=null } // 參數(shù)優(yōu)化 const result=fn.apply(this,args) if(resultCallback) resultCallback(result) lastTime=nowTime return } // 優(yōu)化trailing if(!timer&&trailing){ timer=setTimeout(()=>{ // 參數(shù)優(yōu)化 const result=fn.apply(this,args) if(resultCallback) resultCallback(result) timer=null lastTime=!leading?0:new Date().getTime() },remainTime) } } _throttle.cancel=function(){ if(timer) clearTimeout(timer) timer = null lastTime = 0 } return _throttle }
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
JavaScript實現(xiàn)ASC轉(zhuǎn)漢字及漢字轉(zhuǎn)ASC的方法
這篇文章主要介紹了JavaScript實現(xiàn)ASC轉(zhuǎn)漢字及漢字轉(zhuǎn)ASC的方法,涉及JavaScript編碼轉(zhuǎn)換的相關(guān)技巧,需要的朋友可以參考下2016-01-01用nodejs實現(xiàn)PHP的print_r函數(shù)代碼
這篇文章主要介紹了用nodejs實現(xiàn)PHP的print_r函數(shù)代碼,需要的朋友可以參考下2014-03-03js前端實現(xiàn)圖片懶加載(lazyload)的兩種方式
本篇文章主要介紹了js前端實現(xiàn)圖片懶加載(lazyload)的兩種方式 ,使用圖片懶加載可以提高網(wǎng)頁運行速度,有興趣的可以了解一下。2017-04-04