好吧,一直在秋招中,都沒怎麼寫部落格了。。。今天趕緊來補一補才行。。。我發現,在面試中,講到函式節流好像可以加分,儘管這並不是特別高深的技術,下面就聊聊吧! ^_^
備註:以下內容部分來自《JavaScript高階程式設計》
函式節流的目的
從字面上就可以理解,函式節流就是用來節流函式從而一定程度上優化效能的。例如,DOM 操作比起非DOM 互動需要更多的記憶體和CPU時間。連續嘗試進行過多的DOM 相關操作可能會導致瀏覽器掛起,有時候甚至會崩潰。尤其在IE 中使用onresize 事件處理程式的時候容易發生,當調整瀏覽器大小的時候,該事件會連續觸發。在onresize 事件處理程式內部如果嘗試進行DOM 操作,其高頻率的更改可能會讓瀏覽器崩潰。又例如,我們常見的一個搜尋的功能,我們一般是繫結keyup事件,每按下一次鍵盤就搜尋一次。但是我們的目的主要是每輸入一些內容搜尋一次而已。為了解決這些問題,就可以使用定時器對函式進行節流。
函式節流的原理
某些程式碼不可以在沒有間斷的情況連續重複執行。第一次呼叫函式,建立一個定時器,在指定的時間間隔之後執行程式碼。當第二次呼叫該函式時,它會清除前一次的定時器並設定另一個。如果前一個定時器已經執行過了,這個操作就沒有任何意義。然而,如果前一個定時器尚未執行,其實就是將其替換為一個新的定時器。目的是隻有在執行函式的請求停止了一段時間之後才執行。
函式節流的基本模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
var processor = { timeoutId: null, //實際進行處理的方法 performProcessing: function(){ //實際執行的程式碼 }, //初始處理呼叫的方法 process: function(){ clearTimeout(this.timeoutId); var that = this; this.timeoutId = setTimeout(function(){ that.performProcessing(); }, 100); } }; //嘗試開始執行 processor.process(); |
好吧,看得確實有點迷糊。下面通過一個例子來詳細說明,並且在這個基礎模式之上做一個優化處理。
例子場景:實現常見的搜尋功能
①沒有使用函式節流的情況下,為input繫結keyup事件處理函式,在控制檯輸出我輸入的內容。
測試主要程式碼:
1 |
<input id="search" type="text" name="search"> |
1 2 3 4 5 6 |
function queryData(text){ console.log("搜尋:" + text); } var input = document.getElementById("search"); input.addEventListener("keyup", function(event){ queryData(this.value); }); |
結果如圖:
可以看出,這種情況下,每按下一個鍵盤鍵,就輸出了一次。短短的一些內容,輸出了14次,如果每一次都是一次ajax查詢請求的話就發了14個請求了。在效能上的消耗可想而知。
②使用基本的函式節流模式的情況。
測試主要程式碼:
1 |
<input id="search" type="text" name="search"> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
function queryData(text){ console.log("搜尋:" + text); } var input = document.getElementById("search"); input.addEventListener("keyup", function(event){ throttle(queryData, null, 500, this.value); // queryData(this.value); }); function throttle(fn,context,delay,text){ clearTimeout(fn.timeoutId); fn.timeoutId = setTimeout(function(){ fn.call(context,text); },delay); } |
結果如圖:
可以看出,這種情況下,輸入了好一些內容,只輸出了一次,因為測試的時候設定了兩次輸入間隔是500ms,實際應用可根據情況設定。顯然,這在效能上大大滴得到了優化。不過,這樣的話,有一個新問題,如下圖:
好吧,或許看不出端倪。其實問題就是,假如我不斷地輸入,輸入了很多內容,但是我每兩次之間的輸入間隔都小於自己設定的delay值,那麼,這個queryData搜尋函式就一直得不到呼叫。實際上,我們更希望的是,當達到某個時間值時,一定要執行一次這個搜尋函式。所以,就有了函式節流的改進模式。
③函式節流增強版
測試的主要程式碼:
1 |
<input id="search" type="text" name="search"> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
function queryData(text){ console.log("搜尋:" + text); } var input = document.getElementById("search"); input.addEventListener("keyup", function(event){ throttle(queryData, null, 500, this.value,1000); // throttle(queryData, null, 500, this.value); // queryData(this.value); }); function throttle(fn,context,delay,text,mustApplyTime){ clearTimeout(fn.timer); fn._cur=Date.now(); //記錄當前時間 if(!fn._start){ //若該函式是第一次呼叫,則直接設定_start,即開始時間,為_cur,即此刻的時間 fn._start=fn._cur; } if(fn._cur-fn._start>mustApplyTime){ <br> //當前時間與上一次函式被執行的時間作差,與mustApplyTime比較,若大於,則必須執行一次函式,若小於,則重新設定計時器 fn.call(context,text); fn._start=fn._cur; }else{ fn.timer=setTimeout(function(){ fn.call(context,text); },deley); } } |
測試結果如圖:
顯然,連續的輸入,到一定時間間隔之後,queryData函式必然會被呼叫,但是又不是頻繁的呼叫。這既達到了節流的目的,又不會影響使用者體驗。
④進一步的優化
進一步的話,就是可以在呼叫throttle函式之前,先對輸入的內容進行判斷,若其值為空、值不變都不用再呼叫。這裡就不詳說了。
更多學習連結
好吧,就到這了,如果有錯誤的地方,還要指正哦…^_^
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!
任選一種支付方式