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