最近在作網頁的時候有個需求,就是瀏覽器窗口改變的時候須要改一些頁面元素大小,因而乎很天然的想到了window的resize事件,因而乎我是這麼寫的javascript
DOCTYPE html> <</span>html> <</span>head> <</span>title>Throttle</</span>title> </</span>head> <</span>body> <</span>script type="text/javascript"> n=0; function resizehandler(){ console.log(new Date().getTime()); console.log(++n); } window.onresize=resizehandler; </</span>script> </</span>body> </</span>html>
功能卻是實現了,可是我拖拽的方式改變瀏覽器窗口大小的時候看了下控制檯html
沒錯,簡單的一個拖拽讓個人resizeHandler()方法執行了52次,這徹底不是我想要的效果,實際上個人resizeHandler()方法的代碼很複雜,甚至會使用ajax向服務器發送請求,要是簡單的一次改變窗口大小就要調用52次這還了得java
其實個人本意只是窗口resize後頁面作一些調整就能夠,而window的resize事件並非在resize結束後才觸發的,具體則麼個頻率我也不知道,但倒是在不停的調用,直到窗口大小再也不變化。其實相似的機制還有鼠標的mousemove,都是在短期內重複觸發。ajax
在《JavaScript高級程序設計》中有專門應對此問題的函數節流瀏覽器
function throttle(method,context){ clearTimeout(method.tId); method.tId=setTimeout(function(){ method.call(context); },500); }
原理很簡單,利用定時器,讓函數執行延遲500毫秒,在500毫秒內若是有函數又被調用則刪除上一次調用,此次調用500毫秒後執行,如此往復。這樣我剛纔的代碼能夠改成服務器
拖拽一下試試,果然只執行了一次閉包
網上還有一種函數節流方案,它是這麼作的app
function throttle(method,delay){ var timer=null; return function(){ var context=this, args=arguments; clearTimeout(timer); timer=setTimeout(function(){ method.apply(context,args); },delay); } }
調用一下試試,同樣的效果函數
兩種方法都是利用了setTimeout,不一樣的是第二種方法加入的函數延遲執行時間,這個在第一種方案中很容易也具備此功能,加一個參數的事兒。this
但第一種方案把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);
果然既沒有頻繁執行也沒有就最後執行