什麼是函式節流?
函式節流簡單的來說就是不想讓該函式在很短的時間內連續被呼叫,比如我們最常見的是視窗縮放的時候,經常會執行一些其他的操作函式,比如發一個ajax請求等等事情,那麼這時候視窗縮放的時候,有可能連續發多個請求,這並不是我們想要的,或者是說我們常見的滑鼠移入移出tab切換效果,有時候連續且移動的很快的時候,會有閃爍的效果,這時候我們就可以使用函式節流來操作。大家都知道,DOM的操作會很消耗或影響效能的,如果是說在視窗縮放的時候,為元素繫結大量的dom操作的話,會引發大量的連續計算,比如在IE下,過多的DOM操作會影響瀏覽器效能,甚至嚴重的情況下,會引起瀏覽器崩潰的發生。這個時候我們就可以使用函式節流來優化程式碼了~
函式節流的基本原理:
使用一個定時器,先延時該函式的執行,比如使用setTomeout()這個函式延遲一段時間後執行函式,如果在該時間段內還觸發了其他事件,我們可以使用清除方法 clearTimeout()來清除該定時器,再setTimeout()一個新的定時器延遲一會兒執行。
我們先來看一個簡單的window.resize的demo例子,比如我先定義一個全域性變數count=0;當我觸發一次window.resize的時候,該全域性變數count++; 我們來看看在控制檯中列印出count的效果;JS程式碼如下:
1 2 3 4 5 |
var count = 0; window.onresize = function(){ count++; console.log(count); } |
執行截圖效果如下:
如上resize的程式碼,簡單的縮放一次就列印出多次,這並不是我們想要的效果,這是簡單的測試,那如果我們換成ajax請求的話,那麼就會縮放一次視窗會連續觸發多次ajax請求,下面我們試著使用函式節流的操作試試一下;
函式節流的第一種方案封裝如下:
1 2 3 4 5 6 |
function throttleFunc(method,context){ clearTimeout(method.tId); method.tId = setTimeout(function(){ method.call(context); },100); } |
我們再來封裝一下視窗縮放的demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
var count = 0; function myFunc() { count++; console.log(count); } window.onresize = function(){ throttleFunc(myFunc); } function throttleFunc(method,context){ clearTimeout(method.tId); method.tId = setTimeout(function(){ method.call(context); },100); } |
如上程式碼,我們再來看看效果,視窗縮放和放大效果會看到,只執行了一次;列印了一次。
上面的程式碼使用一個定時器每隔100毫秒執行一次;
我們也可以使用閉包的方法對上面的函式進行再封裝一下;
函式節流的第二種封裝方法如下:
1 2 3 4 5 6 7 8 9 10 11 |
function throttle(fn, delay){ var timer = null; return function(){ var context = this, args = arguments; clearTimeout(timer); timer = setTimeout(function(){ fn.apply(context, args); }, delay); }; }; |
上面第二種方案是使用閉包的方式形成一個私有的作用域來存放定時器timer,第二種方案的timer是通過傳引數的形式引入的。
呼叫demo程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
var count = 0; function myFunc() { count++; console.log(count); } var func = throttle(myFunc,100); window.onresize = function(){ func(); } function throttle(fn, delay){ var timer = null; return function(){ var context = this, args = arguments; clearTimeout(timer); timer = setTimeout(function(){ fn.apply(context, args); }, delay); }; }; |
函式節流的基本思想是:就是想讓一個函式不要執行的太頻繁,減少一些過快的來節流函式,比如當我們改變視窗縮放的時候,瀏覽器的間隔有可能是16ms,這是瀏覽器自帶的時間間隔,我們無法改變,而我們通過節流的方式可以試著改變一下這個間隔,儘量稍微延長下這個呼叫時間,因此我們可以封裝如下函式:
函式節流的第三種封裝方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
function throttle3(fn,delay,runDelay){ var timer = null; var t_start; return function(){ var context = this, args = arguments, t_cur = new Date(); timer & clearTimeout(timer); if(!t_start) { t_start = t_cur; } if(t_cur - t_start >= runDelay) { fn.apply(context,args); t_start = t_cur; }else { timer = setTimeout(function(){ fn.apply(context,args); },delay); } } } |
呼叫demo如下:
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 27 28 29 |
var count = 0; function myFunc() { count++; console.log(count); } var func = throttle3(myFunc,50,100); window.onresize = function(){ func();} function throttle3(fn,delay,runDelay){ var timer = null; var t_start; return function(){ var context = this, args = arguments, t_cur = new Date(); timer & clearTimeout(timer); if(!t_start) { t_start = t_cur; } if(t_cur - t_start >= runDelay) { fn.apply(context,args); t_start = t_cur; }else { timer = setTimeout(function(){ fn.apply(context,args); },delay); } } } |
上面的第三個函式是封裝後的函式,有三個引數,我們可以自己設定觸發事件的時間間隔,則意味著,如上程式碼50ms連續呼叫函式,後一個呼叫會把前一個呼叫的等待處理掉,但每隔100ms會至少執行一次,具體使用哪一種方式只要看自己的權衡,但是我個人覺得第二種封裝函式的方式夠我們使用的,當然據說第三種方式效能更好~