JS效能優化 -- 函式節流
一、函式節流的概念、主要用途及意義
概念:函式節流是通過一個定時器,阻斷連續重複的函式呼叫,從而一定程度上優化效能。
用途:主要用於使用者介面呼叫的函式,如:resize、mousemove、keyup事件的監聽函式。
這類監聽函式的主要特徵:
1、短時間內連續多次重複觸發;
2、大量的DOM操作。意義:在使用者察覺範圍外,降低函式呼叫的頻率,從而提升效能
二、函式節流的原理
某些程式碼不可以在沒有間斷的情況連續重複執行。第一次呼叫函式,建立一個定時器,在指定的時間間隔之後執行程式碼。當第二次呼叫該函式時,它會清除前一次的定時器並設定另一個。如果前一個定時器已經執行過了,這個操作就沒有任何意義。然而,如果前一個定時器尚未執行,其實就是將其替換為一個新的定時器。目的是隻有在執行函式的請求停止了一段時間之後才執行。
三、具體用法
例如:
1、使用onresize 事件處理程式的時候容易發生,當調整瀏覽器大小的時候,該事件會連續觸發。在onresize 事件處理程式內部如果嘗試進行DOM 操作,其高頻率的更改可能會讓瀏覽器崩潰。
2、我們常見的一個搜尋的功能,我們一般是繫結keyup事件,每按下一次鍵盤就搜尋一次。但是我們的目的主要是每輸入一些內容搜尋一次而已。為了解決這些問題,就可以使用定時器對函式進行節流。
基本模式
var processor = {
timeoutId: null,
//實際進行處理的方法
performProcessing: function (){
//實際執行的程式碼
},
//初始處理呼叫的方法
process: function(){
clearTimeout(this.timeoutId);
var _this = this;
this.timeoutId = setTimeout(function(){
_this.performProcessing();
}, 100);
}
};
//嘗試開始執行
processor.process();
下面以keyup為例
1、不使用函式節流的情況
<input id="search" type="text" name="search">
function queryData(text){
console.log("搜尋:" + text);
}
var input = document.getElementById("search");
input.addEventListener("keyup", function(event){ queryData(this.value);
});
結果如圖:
2、使用基本函式節流的情況
<input id="search" type="text" name="search">
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);
}
結果如圖:
實際上,我們更希望的是,當達到某個時間值時,一定要執行一次這個搜尋函式。所以,就有了函式節流的改進模式。
3、函式節流增強版
<input id="search" type="text" name="search">
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){
//當前時間與上一次函式被執行的時間作差,與mustApplyTime比較,若大於,則必須執行一次函式,若小於,則重新設定計時器
fn.call(context,text);
fn._start=fn._cur;
}else{
fn.timer=setTimeout(function(){
fn.call(context,text);
},delay);
}
}
結果如圖:
顯然,連續的輸入,到一定時間間隔之後,queryData函式必然會被呼叫,但是又不是頻繁的呼叫。這既達到了節流的目的,又不會影響使用者體驗。
4、進一步的優化
進一步的話,就是可以在呼叫throttle函式之前,先對輸入的內容進行判斷,若其值為空、值不變都不用再呼叫。