在《JavaScript高級程序設計》一書有介紹函數節流,里面封裝了這樣一個函數節流函數:
function throttle(method, context) {
clearTimeout(methor.tId);
method.tId = setTimeout(function(){
method.call(context);
}, 100);
}
它把定時器ID存為函數的一個屬性。而調用的時候就直接寫
window.onresize = function(){
throttle(myFunc);
}
impress用的是另一個封裝函數:
var throttle = function(fn, delay){
var timer = null;
return function(){
var context = this, args = arguments;
clearTimeout(timer);
timer = setTimeout(function(){
fn.apply(context, args);
}, delay);
};
};
它使用閉包的方法形成一個私有的作用域來存放定時器變量timer。而調用方法為
window.onresize = throttle(myFunc, 100);
兩種方法各有優劣,前一個封裝函數的優勢在把上下文變量當做函數參數,直接可以定制執行函數的this變量;后一個函數優勢在於把延遲時間當做變量(當然,前一個函數很容易做這個拓展),而且個人覺得使用閉包代碼結構會更優,且易於拓展定制其他私有變量,缺點就是雖然使用apply把調用throttle時的this上下文傳給執行函數,但畢竟不夠靈活。
上面介紹的函數節流,它這個頻率就不是50ms之類的,它就是無窮大,只要你能不間斷resize,刷個幾年它也一次都不執行處理函數。我們可以對上面的節流函數做拓展:
var throttleV2 = function(fn, delay, mustRunDelay){
var timer = null;
var t_start;
return function(){
var context = this, args = arguments, t_curr = +new Date();
clearTimeout(timer);
if(!t_start){
t_start = t_curr;
}
if(t_curr - t_start >= mustRunDelay){
fn.apply(context, args);
t_start = t_curr;
}
else {
timer = setTimeout(function(){
fn.apply(context, args);
}, delay);
}
};
};
在這個拓展后的節流函數升級版,我們可以設置第三個參數,即必然觸發執行的時間間隔。如果用下面的方法調用
window.onresize = throttleV2(myFunc, 50, 100);
則意味着,50ms的間隔內連續觸發的調用,后一個調用會把前一個調用的等待處理掉,但每隔100ms至少執行一次。原理也很簡單,打時間tag,一開始記錄第一次調用的時間戳,然后每次調用函數都去拿最新的時間跟記錄時間比,超出給定的時間就執行一次,更新記錄時間。
