理解JavaScript中的去抖函數

來源:互聯網
上載者:User

標籤:實現   私人   事件   ext   var   應用   sed   arguments   trail   

何為去抖函數?在學習JavaScript去抖函數之前我們需要先弄明白這個概念。很多人都會把去抖跟節流兩個概念弄混,但是這兩個概念其實是很好理解的。

去抖函數(Debounce Function),是一個可以限制指定函數觸發頻率的函數。我們可以理解為連續調用同一個函數多次,只得到執行該函數一次的結果;但是隔一段時間再次調用時,又可以重新獲得新的結果,具體這段時間有多長取決於我們的設定。這種函數的應用情境有哪些呢?

比如我們寫一個DOM事件監聽函數,

window.onscroll = function(){    console.log(‘Got it!‘);}

  

現在當我們滑動滑鼠滾輪的時候,我們就可以看到事件被觸發了。但是我們可以發現在我們滾動滑鼠滾輪的時候,我們的控制台在不斷的列印訊息,因為window的scroll事件被我們不斷的觸發了。

在當前情境下,可能這是一個無傷大雅的行為,但是可以預見到,當我們的事件監聽函數(Event Handler)涉及到一些複雜的操作時(比如Ajax請求、DOM渲染、大量資料計算),會對電腦效能產生多大影響;在一些比較老舊的機型或者較低版本的瀏覽器(尤其IE)中,很可能會導致死機情況的出現。所以這個時候我們就要想辦法,在指定時間段內,只執行一定次數的事件處理函數。

理解去抖函數

說了一些概念和應用情境,但是還是很拗口,到底什麼是去抖函數?

我們可以通過如下執行個體來理解:

假設有以下代碼:

var debounce = function(callback, delay, immediate){    var timeout, result;    return function(){        var callNow;        if(timeout)            clearTimeout(timeout);        callNow = !timeout && immediate;        if(callNow) {            result = callback.apply(this, Array.prototype.slice.call(arguments, 0));            timeout = {};        }        else {            timeout = setTimeout(()=>{                callback.apply(this, Array.prototype.slice.call(arguments, 0));            }, delay);        }    };};var s = debounce(()=>{    console.log(‘yes...‘);}, 2000);window.onscroll = s;

  

debounce函數就是我自己實現的一個簡單的去抖函數,我們可以通過這段代碼進行實驗。

步驟如下:

  • 複製以上代碼,開啟瀏覽器,開啟控制台(F12),然後粘貼代碼並斷行符號執行。
  • 連續不斷的滾動滑鼠,查看控制台有無輸出。
  • 停止滾動滑鼠,2s之內再次滾動滑鼠,查看是否有輸出。
  • 連續滾動之後停止2s以上,查看是否有輸出。

通過以上步驟,我們可以發現當我們連續滾動滑鼠時,控制台沒有訊息被列印出來,停止2s以內並再次滾動時,也沒有訊息輸出;但是當我們停止的時間超過2s時,我們可以看到控制台有訊息輸出。

這就是去抖函數。在連續的觸發中(無論時間長度),只能得到觸發一次的效果。在指定時間長度內連續觸發,最多隻能得到一次觸發的效果。

underscore的實現

underscore源碼如下(附代碼注釋):

// Returns a function, that, as long as it continues to be invoked, will not// be triggered. The function will be called after it stops being called for// N milliseconds. If `immediate` is passed, trigger the function on the// leading edge, instead of the trailing.//去抖函數,傳入的函數在wait時間之後(或之前)執行,並且只會被執行一次。//如果immediate傳遞為true,那麼在函數被傳遞時就立即調用。//實現原理:涉及到非同步JavaScript,多次調用_.debounce返回的函數,會一次性執行完,但是每次調用//該函數又會清空上一次的TimeoutID,所以實際上只執行了最後一個setTimeout的內容。_.debounce = function (func, wait, immediate) {var timeout, result;var later = function (context, args) {timeout = null;//如果沒有傳遞args參數,那麼func不執行。if (args) result = func.apply(context, args);};//被返回的函數,該函數只會被調用一次。var debounced = restArgs(function (args) {//這行代碼的作用是清除上一次的TimeoutID,//使得如果有多次調用該函數的情境時,只執行最後一次調用的延時。if (timeout) clearTimeout(timeout);if (immediate) {////如果傳遞了immediate並且timeout為空白,那麼就立即調用func,否則不立即調用。var callNow = !timeout;//下面這行代碼,later函數內部的func函數註定不會被執行,因為沒有給later傳遞參數。//它的作用是確保返回了一個timeout。timeout = setTimeout(later, wait);if (callNow) result = func.apply(this, args);} else {//如果沒有傳遞immediate,那麼就使用_.delay函數延時執行later。timeout = _.delay(later, wait, this, args);}return result;});//該函數用於取消當前去抖效果。debounced.cancel = function () {clearTimeout(timeout);timeout = null;};return debounced;};

  

可以看到underscore使用了閉包的方法,定義了兩個私人屬性:timeout和result,以及兩個私人方法later和debounced。最終會返回debounced作為處理之後的函數。timeout用於接受並儲存setTimeout返回的TimeoutID,result用於執行使用者傳入的func函數的執行結果,later方法用於執行傳入的func函數。

實現原理

利用了JavaScript的非同步執行機制,JavaScript會優先執行完所有的同步代碼,然後去事件隊列中執行所有的非同步任務。

當我們不斷的觸發debounced函數時,它會不斷的clearTimeout(timeout),然後再重新設定新的timeout,所以實際上在我們的同步代碼執行完之前,每次調用debounced函數都會重設timeout。所以非同步事件隊列中的非同步任務會不斷重新整理,直到最後一個debounced函數執行完。只有最後一個debounced函數設定的later非同步任務會在同步代碼執行之後被執行。

所以當我們在之前實驗中不斷的滾動滑鼠時,實際上是在不斷的調用debounced函數,不斷的清除timeout對應的非同步任務,然後又設定新的timeout非同步任務。當我們停止的時間不超過2s時,timeout對應的非同步任務還沒有被觸發,所以再次滾動滑鼠觸發debounced函數還可以清除timeout任務然後設定新的timeout任務。一旦停止的時間超過2s,最終的timeout對應的非同步代碼就會被執行。

總結
  • 去抖是限制函數執行頻率的一種方法。
  • 去抖後的函數在指定時間內最多被觸發一次,連續觸發去抖後的函數只能得到一次的觸發效果。
  • underscore去抖的實現依賴於JavaScript的非同步執行機制,優先執行同步代碼,然後執行事件隊列中的非同步代碼。
參考
  • underscore 函數去抖的實現
  • JavaScript Debounce Function
  • Stack Overflow:What does _.debounce do?

 

更多underscore源碼解讀:GitHub

理解JavaScript中的去抖函數

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.