標籤:time() 監聽 問題 under 佔用 last 函數 像素 文字
帶著問題去嘗試
首先我們要知道為什麼要用到函數節流和函數去抖?我們帶著以下的疑問來進行分析!
1.比如搜尋方塊,你會用到什麼事件(change、blur、keyup等)?去做什麼效果?
2.再比如scroll滾動事件,怎麼去觸發?是滾一段距離觸發一次?還是滾一圈觸發一次?還是滾一次觸發一次?
3.還包括mouseover事件是怎麼觸發呢?
......
情境執行個體
函數節流和去抖的出現情境,一般都伴隨著用戶端 DOM 的事件監聽。舉個例子,實現一個原生的拖拽功能(不能用 H5 Drag&Drop API),需要一路監聽 mousemove 事件,在回調中擷取元素當前位置,然後重設 dom 的位置(樣式改變)。如果我們不加以控制,每移動一定像素而觸發的回調數量是會非常驚人的,回調中又伴隨著 DOM 操作,繼而引發瀏覽器的重排與重繪,效能差的瀏覽器可能就會直接假死,這樣的使用者體驗是非常糟糕的。我們需要做的是降低觸發回調的頻率,比如讓它 500ms 觸發一次,或者 200ms,甚至 100ms,這個閾值不能太大,太大了拖拽就會失真,也不能太小,太小了低版本瀏覽器可能就會假死,這樣的解決方案就是函數節流,英文名字叫「throttle」。函數節流的核心是,讓一個函數不要執行得太頻繁,減少一些過快的調用來節流。
說完函數節流,再看它的好基友函數去抖(debounce)。思考這樣一個情境,對於瀏覽器視窗,每做一次 resize 操作,發送一個請求,很顯然,我們需要監聽 resize 事件,但是和 mousemove 一樣,每縮小(或者放大)一次瀏覽器,實際上會觸發 N 多次的 resize 事件,用節流?節流只能保證定時觸發,我們一次就好,這就要用去抖。簡單的說,函數去抖就是對於一定時間段的連續的函數調用,只讓其執行一次。
執行個體瞭解
下面就讓我們來瞭解throttle(節流) 和 debounce(去抖)
1.throttle節流,scroll和mouseover亦或者其他方式,每次移動都會觸發代碼效果,但是這些效果太密集了,佔用了系統資源,電腦的計算能力是有限的,我們寫的代碼應該儘可能的優雅。
2.debounce去抖,本意是從按鍵去抖過來的,讓我們的使用者正確的操作之後才去執行代碼,且只會執行一次,比如當我們做文本輸入的時候,如果用keyup我們不是去節流,而是去判斷使用者停止了輸入。
3.節流保證在一定時間內,只能觸發一次。如果我們只要觸發一次,這就是去抖。代碼上來看差距不是很大。
直接試一下:
//先來個有問題的代碼window.onscroll=function(){ console.log(new Date());}
控制台會列印:
VM109:2 Sun Mar 12 2017 18:05:50 GMT+0800 (中國標準時間)VM109:2 Sun Mar 12 2017 18:05:50 GMT+0800 (中國標準時間)VM109:2 Sun Mar 12 2017 18:05:50 GMT+0800 (中國標準時間)VM109:2 Sun Mar 12 2017 18:05:50 GMT+0800 (中國標準時間)VM109:2 Sun Mar 12 2017 18:05:50 GMT+0800 (中國標準時間)VM109:2 Sun Mar 12 2017 18:05:50 GMT+0800 (中國標準時間)
...
控制台會一直這樣輸入,那問題來了,這擷取並不是我們想要的,我們並不想讓他一直觸發下去。
我們要解決這些問題,所以直接看代碼:
//我們先來個節流,每秒輸出一次window.onscroll=function(){ if(window.onScrollTag == undefined){ window.onScrollTag=true; console.log(new Date()); setTimeout(function(){ window.onScrollTag=undefined; },1000); }}//先觸發window.onscroll=function(){ if(window.onScrollTag == undefined){ window.onScrollTag=setTimeout(function(){ window.onScrollTag=undefined; console.log(new Date()); },1000); }}//後觸發//從上面的代碼可以看出來,我們在一個事件執行的時候就把標記改成不可用。一秒後才可以再次觸發操作。//我們在嘗試一下去抖window.onscroll=function(){ if(window.onScrollTag != undefined){ clearTimeout(window.onScrollTag); } window.onScrollTag=setTimeout(function(){ console.log(new Date()); },1000);}//消抖,消除抖動,感覺這個更好聽
//節流funtion throttle(func, wait) { var lastTime = 0, timeout; return funtion() { var context = this, args = arguments, delay = wait || 100, now = new Date().getTime(); if (now - lastTime <= delay) { return; } lastTime = now; clearTimeout(timeout); timeout = setTimeout(function() { timeout = null; func.apply(context, args); }, delay) }}// 去抖function debounce(func, wait) { var timeout; return function() { var context = this, args = arguments, delay = wait || 250; clearTimeout(timeout); timeout = setTimeout(function() { timeout = null; func.apply(context, args); }, delay); };}
throttle 應用情境
函數節流有哪些應用情境?哪些時候我們需要間隔一定時間觸發回調來控制函數調用頻率?
- DOM 元素的拖拽功能實現(mousemove)
- 射擊遊戲的 mousedown/keydown 事件(單位時間只能發射一顆子彈)
- 計算滑鼠移動的距離(mousemove)
- Canvas 類比畫板功能(mousemove)
- 搜尋聯想(keyup)
- 監聽滾動事件判斷是否到頁面底部自動載入更多:給 scroll 加了 debounce 後,只有使用者停止滾動後,才會判斷是否到了頁面底部;如果是 throttle 的話,只要頁面滾動就會間隔一段時間判斷一次
debounce 應用情境
函數去抖有哪些應用情境?哪些時候對於連續的事件響應我們只需要執行一次回調?
- 每次 resize/scroll 觸發統計事件
- 文本輸入的驗證(連續輸入文字後發送 AJAX 請求進行驗證,驗證一次就好)
小結
函數節流和函數去抖的核心其實就是限制某一個方法被頻繁觸發,而一個方法之所以會被頻繁觸發,大多數情況下是因為 DOM 事件的監聽回調,而這也是函數節流以及去抖多數情況下的應用情境
如果大家感興趣,可以參考下:http://underscorejs.org/
javascript中的函數節流和函數去抖