原文作者:小蚊
原文地址:https://www.cnblogs.com/LuckyWinty/p/5949970.html
好吧,一直在秋招中,都沒怎么寫博客了。。。今天趕緊來補一補才行。。。我發(fā)現(xiàn),在面試中,講到函數(shù)節(jié)流好像可以加分,盡管這并不是特別高深的技術,下面就聊聊吧! _
備注:以下內容部分來自《JavaScript高級程序設計》
函數(shù)節(jié)流的目的
從字面上就可以理解,函數(shù)節(jié)流就是用來節(jié)流函數(shù)從而一定程度上優(yōu)化性能的。例如,DOM 操作比起非DOM 交互需要更多的內存和CPU 時間。連續(xù)嘗試進行過多的DOM 相關操作可能會導致瀏覽器掛起,有時候甚至會崩潰。尤其在IE 中使用onresize 事件處理程序的時候容易發(fā)生,當調整瀏覽器大小的時候,該事件會連續(xù)觸發(fā)。在onresize 事件處理程序內部如果嘗試進行DOM 操作,其高頻率的更改可能會讓瀏覽器崩潰。又例如,我們常見的一個搜索的功能,我們一般是綁定keyup事件,每按下一次鍵盤就搜索一次。但是我們的目的主要是每輸入一些內容搜索一次而已。為了解決這些問題,就可以使用定時器對函數(shù)進行節(jié)流。
函數(shù)節(jié)流的原理
某些代碼不可以在沒有間斷的情況連續(xù)重復執(zhí)行。第一次調用函數(shù),創(chuàng)建一個定時器,在指定的時間間隔之后運行代碼。當?shù)诙握{用該函數(shù)時,它會清除前一次的定時器并設置另一個。如果前一個定時器已經(jīng)執(zhí)行過了,這個操作就沒有任何意義。然而,如果前一個定時器尚未執(zhí)行,其實就是將其替換為一個新的定時器。目的是只有在執(zhí)行函數(shù)的請求停止了一段時間之后才執(zhí)行。
函數(shù)節(jié)流的基本模式
var processor = {
timeoutId: null,
//實際進行處理的方法
performProcessing: function(){
//實際執(zhí)行的代碼
},
//初始處理調用的方法
process: function(){
clearTimeout(this.timeoutId);
var that = this;
this.timeoutId = setTimeout(function(){
that.performProcessing();
}, 100);
}
};
//嘗試開始執(zhí)行
processor.process();
好吧,看得確實有點迷糊。下面通過一個例子來詳細說明,并且在這個基礎模式之上做一個優(yōu)化處理。
例子場景:實現(xiàn)常見的搜索功能
①沒有使用函數(shù)節(jié)流的情況下,為input綁定keyup事件處理函數(shù),在控制臺輸出我輸入的內容。
測試主要代碼:
HTMl:
<input id="search" type="text" name="search">
JS:
<script>
function queryData(text){
console.log("搜索:" + text);
}
var input = document.getElementById("search");
input.addEventListener("keyup", function(event){ queryData(this.value);
});
</script>
結果如圖:

可以看出,這種情況下,每按下一個鍵盤鍵,就輸出了一次。短短的一些內容,輸出了14次,如果每一次都是一次ajax查詢請求的話就發(fā)了14個請求了。在性能上的消耗可想而知。
②使用基本的函數(shù)節(jié)流模式的情況。
測試主要代碼:
HTML:
<input id="search" type="text" name="search">
JS:
<script>
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);
}
</script>
結果如圖:

可以看出,這種情況下,輸入了好一些內容,只輸出了一次,因為測試的時候設置了兩次輸入間隔是500ms,實際應用可根據(jù)情況設置。顯然,這在性能上大大滴得到了優(yōu)化。不過,這樣的話,有一個新問題,如下圖:

好吧,或許看不出端倪。其實問題就是,假如我不斷地輸入,輸入了很多內容,但是我每兩次之間的輸入間隔都小于自己設置的delay值,那么,這個queryData搜索函數(shù)就一直得不到調用。實際上,我們更希望的是,當達到某個時間值時,一定要執(zhí)行一次這個搜索函數(shù)。所以,就有了函數(shù)節(jié)流的改進模式。
③函數(shù)節(jié)流增強版
測試的主要代碼:
HTML:
<input id="search" type="text" name="search">
JS:
<script>
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){ //若該函數(shù)是第一次調用,則直接設置_start,即開始時間,為_cur,即此刻的時間
fn._start=fn._cur;
}
if(fn._cur-fn._start>mustApplyTime){
//當前時間與上一次函數(shù)被執(zhí)行的時間作差,與mustApplyTime比較,若大于,則必須執(zhí)行一次函數(shù),若小于,則重新設置計時器
fn.call(context,text);
fn._start=fn._cur;
}else{
fn.timer=setTimeout(function(){
fn.call(context,text);
},deley);
}
}
</script>
測試結果如圖:

顯然,連續(xù)的輸入,到一定時間間隔之后,queryData函數(shù)必然會被調用,但是又不是頻繁的調用。這既達到了節(jié)流的目的,又不會影響用戶體驗。
④進一步的優(yōu)化
進一步的話,就是可以在調用throttle函數(shù)之前,先對輸入的內容進行判斷,若其值為空、值不變都不用再調用。這里就不詳說了。
更多學習鏈接
好吧,就到這了,如果有錯誤的地方,還要指正哦..._