摘要:節(jié)流和防抖都是用來提高用戶體驗,提高網(wǎng)站性能的手段,它們的技術手段都是強制事件處理函數(shù)在特定的事件段內(nèi)執(zhí)行。針對上面舉例的情況,其實運用節(jié)流和防抖都可以做到,只是它們之間又有一定的區(qū)別節(jié)流節(jié)流是在一定的時間段內(nèi),函數(shù)最多可以被調(diào)用多少次。
“節(jié)流”和“防抖”都是用來提高用戶體驗,提高網(wǎng)站性能的手段,它們的技術手段都是“強制事件處理函數(shù)在特定的事件段內(nèi)執(zhí)行”。這樣解釋可能還是不夠直觀。舉兩個例子吧:
1: 比方說我們給document綁定了一個scroll的事件,scroll事件是每滑動一個px,scroll的處理函數(shù)就會被調(diào)用執(zhí)行,如果在你的處理函數(shù)里面恰巧做了一個很花時間或者很花空間的事情,比方說復雜的運算啊,ajax請求啊,那這樣頁面就可能出現(xiàn)卡頓的情況。
2: 頁面上有個地址的輸入框,你希望根據(jù)客戶的輸入內(nèi)容,去幫客戶補全。假如說這個地址列表需要通過ajax請求來獲取,那我們一定是希望在客戶停止輸入了之后再去請求ajax然后來補全,而不是客戶一邊輸入就一直請求ajax。
針對上面舉例的情況,其實運用節(jié)流和防抖都可以做到,只是它們之間又有一定的區(qū)別:
節(jié)流:節(jié)流是在一定的時間段內(nèi),函數(shù)最多可以被調(diào)用多少次。也可以理解為函數(shù)以一定的頻率被調(diào)用。
防抖:防抖是每次想要執(zhí)行這個函數(shù),都得事先等上一段時間。
語言總是這樣蒼白,直接來看代碼的實現(xiàn)吧。我們先來實現(xiàn)一個防抖:
//實現(xiàn)防抖函數(shù) function debouncing(fn, waitTime){ let timer = undefined; return function(){ let context = this; let args = arguments; clearTimeout(timer); timer = setTimeout(function(){ fn.apply(context, args); }, waitTime) } } //scroll事件的處理函數(shù) function scrollHandler(event){ console.log(new Date(event.timeStamp)); } //document的scroll事件上使用防抖函數(shù) document.addEventListener("scroll", debouncing(scrollHandler,100), false);
實現(xiàn)防抖函數(shù)的核心就是每次事件被觸發(fā)的時候,我們不是立即去調(diào)用相應的handler,而是每一次都重新設置一個timeout,等待一段時間,然后再執(zhí)行我們的handler.
2: 現(xiàn)在來嘗試實現(xiàn)一個節(jié)流函數(shù):
function throttling(fn, intervalTime){ let inInterval = false; return function(){ let context = this; let args = arguments; if(!inInterval) { fn.apply(context, args); inInterval = true; setTimeout(function(){ inInterval = false; }, intervalTime) } } } function scrollHandler(event){ console.log(new Date(event.timeStamp)); } document.addEventListener("scroll", throttling(scrollHandler,500), false);
節(jié)流的核心是管理一個布爾值開關變量(inInterval),在某種條件下切換它的true值和false值,而真正的事件處理函數(shù)只在這個開關變量的某一個值的時候才執(zhí)行,從而達到節(jié)流的目的。
節(jié)流函數(shù)它的實現(xiàn)有很多種,多種就在于控制這個開關變量的值的條件,會不一樣。在上面的例子里,我通過setTimeout的方式,間斷性的來改變inInterval的值。
現(xiàn)在來詳細分析一下上面的實現(xiàn):
1: 第一次scroll事件觸發(fā)的時候,scrollHandler就被立即執(zhí)行了,這個是我個人的一個考慮,希望對于第一次的事件觸發(fā)能馬上有一個回饋給客戶。
2: 但是當?shù)谝淮螆?zhí)行完了之后,我們馬上把"inInterval=true", 假如這時候第二次scroll觸發(fā),代碼執(zhí)行到 if(!inInterval),因為條件表達式為false,所以scrollHandler不會被立即執(zhí)行。那可能之后的第三次,第四次。。。scroll事件觸發(fā)的時候,inInterval都是true,都不會執(zhí)行處理函數(shù)。
3: 我們之前在把inInterval設置為true之后,同時設置了一個timeout,在經(jīng)過一定的時間(intervalTime)之后,inInterval會被設置為false; 假如在這之后馬上又觸發(fā)了一次scroll事件,代碼走到if(!inInterval),條件為true,scrollHandler就可以被執(zhí)行了。
但是上面的代碼存在一個問題,假如我們最后一次的scroll事件,正好發(fā)生在這個循環(huán)時間內(nèi),那它就永遠得不到執(zhí)行了。這個可能會是一個隱藏的bug, 比方說你在進行一次拖拽事件,那目標元素可能永遠都拖不到目的地。
所以我們要改一下代碼,讓最后一次事件能被執(zhí)行:
function throttling(fn, intervalTime){ let inInterval = false; let lastTimer = undefined; let timer = undefined; return function(){ let context = this; let args = arguments; if(!inInterval) { clearTimeout(lastTimer); //這行代碼很重要 fn.apply(context, args); inInterval = true; timer = setTimeout(function(){ inInterval = false; }, intervalTime) }else{ clearTimeout(lastTimer); lastTimer = setTimeout(function(){ fn.apply(fn, args); inInterval = false; }, intervalTime); } } } function scrollHandler(event){ console.log(new Date(event.timeStamp)); } document.addEventListener("click", throttling(scrollHandler,1000), false);
上面的代碼,要特別解釋一下一下這一行代碼:clearTimeout(lastTimer); //這行代碼很重要
如果我們不加這行代碼的話,會出現(xiàn)先點擊的click事件反而后執(zhí)行的問題。比如;我們的intervalTime設置為10s, 然后我們分別在第0s, 第5秒,第12秒都進行一次點擊,我們通過console.log(new Date(event.timeStamp)) 打印每一次事件發(fā)生時的時間, 我們會看到第5秒的那個click事件會比第12秒的那個click后輸出,這就說明這里有問題。
所以我們要在if(!inInterval){}里面把lastTimer給清掉,也就是通過clearTimeout(lastTimer); 這行代碼。
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/83768.html
摘要:節(jié)流節(jié)流限制了一個函數(shù)可以在短時間內(nèi)被調(diào)用的次數(shù)。更新防抖防抖確保了一個函數(shù)只有在一個固定時間段內(nèi)沒有被調(diào)用過后,才會再次被調(diào)用。再換句話說防抖會等待事件不再高頻發(fā)生,再觸發(fā)。這個網(wǎng)站很好的可視化了節(jié)流與防抖。 節(jié)流 Throttling 節(jié)流限制了一個函數(shù)可以在短時間內(nèi)被調(diào)用的次數(shù)。可以這樣形容:在一毫秒內(nèi)最多執(zhí)行此函數(shù) 1 次。 Throttling enforces a maxi...
摘要:封裝方法也比較簡單,書中對此問題也進行了處理使用定時器,讓函數(shù)延遲秒后執(zhí)行,在此秒內(nèi),然后函數(shù)再次被調(diào)用,則刪除上次的定時器,取消上次調(diào)用的隊列任務,重新設置定時器。 在實際開發(fā)中,函數(shù)一定是最實用最頻繁的一部分,無論是以函數(shù)為核心的函數(shù)式編程,還是更多人選擇的面向對象式的編程,都會有函數(shù)的身影,所以對函數(shù)進行深入的研究是非常有必要的。 函數(shù)節(jié)流 比較直白的說,函數(shù)節(jié)流就是強制規(guī)定一...
摘要:相反,在討論時,面試中通常會提到三件事。而認為最后一個參賽者說了算,只要還能吃的,就重新設定新的定時器。試想,如果用戶的操作十分頻繁他每次都不等設置的時間結束就進行下一次操作,于是每次都為該用戶重新生成定時器,回調(diào)函數(shù)被延遲了不計其數(shù)次。本文不是討論最新的 JavaScript 庫、常見的開發(fā)實踐或任何新的 ES6 函數(shù)。相反,在討論 JavaScript 時,面試中通常會提到三件事。我自己...
摘要:更高效的解決方案是將一個事件偵聽器實際綁定到父容器上,然后在實際單擊時可以訪問每個確切元素。如果將事件偵聽器綁定到窗口滾動事件上,并且用戶快速滾動頁面,事件很可能會在短時間多次觸發(fā)。 原文鏈接 問題 #1: 事件委托 事件委托,也叫事件委派,事件代理。 當構建應用程序時,有時需要將事件監(jiān)聽器綁定到頁面上的某些元素上,以便在用戶與元素交互時執(zhí)行某些操作。 假設我們現(xiàn)在有一個無序列表: ...
摘要:譯通過實例講解和防抖與節(jié)流源碼中推薦的文章,為了學習英語,翻譯了一下原文鏈接作者本文來自一位倫敦前端工程師的技術投稿。首次或立即你可能發(fā)現(xiàn)防抖事件在等待觸發(fā)事件執(zhí)行,直到事件都結束后它才執(zhí)行。 [譯]通過實例講解Debouncing和Throtting(防抖與節(jié)流) lodash源碼中推薦的文章,為了學習(英語),翻譯了一下~ 原文鏈接 作者:DAVID CORBACHO 本文來自一位...
閱讀 2219·2021-11-22 13:54
閱讀 3382·2019-08-29 12:25
閱讀 3445·2019-08-28 18:29
閱讀 3590·2019-08-26 13:40
閱讀 3280·2019-08-26 13:32
閱讀 966·2019-08-26 11:44
閱讀 2236·2019-08-23 17:04
閱讀 2977·2019-08-23 17:02