標籤:兩種 應用 封裝 source 應對 情境 調用 ati 總結
對於瀏覽器視窗大小改變的時候,來動態改變頁面元素的大小,可以採用window的resize事件,實現代碼:
<script type="text/javascript"> var n = 0; function resizehandler(){ console.log(new Date().getTime()); console.log(++n); } window.onresize = resizehandler;</script>
功能能夠實現,都是當我們用拖拽的方式改變瀏覽器大小的時候,控制台會不斷列印執行resizehandler的函數的結果。
一次簡單的拖拽會讓resizehandler()函數執行很多次,實際在顯示項目中resizehandler函數可能會很複雜,甚至會涉及到前後端的資料互動,所以一次拖拽執行很多次很明顯是不能夠接受的。
函數去抖
其實我們的本意只是視窗resize後頁面做一些調整就可以了,而window的resize事件並不是在resize結束後才出發,具體的觸發頻率不是很清楚,但卻在不停地調用,直到視窗大小不在變化。類似的機制還有滑鼠的mousemove,都是在短時間內重複觸發。
在《JavaScript進階程式設計》中有專門應對此問題的函數防抖
function throttle(method, context){ clearTimeout(method.tId); method.tId = setTimeout(function(){ method.call(context); }, 500);}
原理很簡單,利用定時器,讓函數執行延遲500毫秒,在500毫秒內如果有函數又被調用則刪除上一次的調用,這次調用500毫秒後執行,如此往複。這樣剛才的代碼可以改為:
<script type="text/javascript"> var n = 0; function resizehandler(){ console.log(new Date().getTime()); console.log(++n); } function throttle(method, context){ clearTimeout(method.tId); method.tId = setTimeout(function(){ method.call(context); }, 500); } window.onresize = function(){ throttle(resizehandler, window); };</script>
這樣的話執行就沒有問題了。
函數防抖的另一種方法
預先設定一個執行循環,當調用動作的時刻大於等於執行循環則執行該動作,然後進入下一個周期。
function throttle(method, dalay){ var timer = null; return function(){ var context = this, args = arguments; clearTimeout(timer); timer = setTimeout(function(){ method.apply(context, args); }, delay); }}
調用一下試試,一樣的效果
<script type="text/javascript"> var n = 0; function resizehandler(){ console.log(new Date().getTime()); console.log(++n); } function throttle(method, delay){ var timer = null; return function(){ var context = this, args = arguments; clearTimeout(timer); timer = setTimeout(function(){ method.apply(context, args); }, delay); } } window.onresize = throttle(resizehandler, 500); //這裡因為返回函數控制代碼,不用封裝函數了。</script>
比較
兩種方法都是利用了setTimeout,不同的是第二種方法加入的函數順延強制時間,這個在第一種方案中很容易也具有的功能,無非是加一個參數。
但是第一種方案把tId設為函數的一個變數儲存,而第二種建立了閉包儲存。個人覺得差距不大,很喜歡第一種,簡單,高效。
新需求
百度首頁輸入自動提示一樣的東西,我在text上綁定keyup事件,每次鍵盤彈起的時候自動提示,但是又不想提示那麼頻繁,於是我用了上面方法,但是悲劇了,只有挺直輸入等500毫秒才會提示,在輸入過程中根本就沒有提示。看了一下代碼,可不是嘛,只有使用者會盲打,在500毫秒內按一下鍵盤,提示函數就會不斷被延遲,這樣只有停下來的時候才會提示,這就沒有意義了。
能不能在函數節流的基礎上間隔固定時間就執行一次?
函數節流
在網上搜了一下我們可以根據第二種下發(第一種函數拓展多個變數感覺有些不好)做些改動,添加一個參數作為到固定間隔必須執行。
function throttle(method, delay, duration){ var timer = null, begin = new Date(); return function(){ var context = this, args = arguments, current = new Date(); clearTimeout(timer); if(current-begin >= duration){ method.apply(context, args); begin = current; } else { timer = setTimeout(function(){ method.apply(context, args); }, delay); } }}
這樣每次我們判斷間隔了夠久,要是超過設定時間則立即執行一次,以剛才的例子試一試效果
window.onresize = throttle(resizehandler, 100, 200);
這樣,既沒有頻繁執行也沒有就最後執行。
總結
對於函數節流在做動態響應使用者行為方面有較大的使用頻率,具體使用基礎版本的函數節流還是改動版本的還要根據業務情境進行具體分析。
throttle和debounce均是通過減少實際邏輯處理過程的執行來提高事件處理函數運行效能的手段,並沒有實質上減少事件的觸發次數。 (逃)
JavaScript函數節流(throttle)與函數去抖(debounce)