js節流和防抖別看這一篇
阿新 • • 發佈:2019-01-10
前言:我們在做頁面事件繫結的時候,經常要進行節流處理,比如滑鼠非同步點選,去執行一個非同步請求時,需要讓它在上一次沒執行時不能再點選,又或者繫結滾動事件,這種持續觸發進行dom判斷的時候,就要按一定頻率的執行。
一、 偽理論:
概念: 節流和防抖我認為都可以稱之為節流。區別:防抖就是不想持續操作的節流,節流就是想固定頻率執行的。
節流:函式節流是指一定時間內js方法只跑一次。前一次沒結束,後一次不會跑。比如滾動條這種持續按頻率觸發。
防抖:函式防抖是指一定時間內js方法只跑一次。前一次沒結束,後一次觸發會按後一次的新間隔時間計算。比如按鈕持續點選的限速。
二、原始的節流操作:
<!-- 一、最原始進行節流操作的函式 --> <script type="text/javascript"> window.onload = function(){ /**基本防抖案例:先清除,後setTimeout進行執行**/ document.querySelector("#send").addEventListener("click",function(e){ clearTimeout(window.mytime_01); window.mytime_01 = setTimeout(function(){ console.log("發射..."); }, 500); }); /**基本節流案例:setTimeout執行完時,恢復標誌位,下一次才能執行*/ var mytime_02 = true; window.onscroll = function(e) { if(!mytime_02){return;} //首次進入能執行 mytime_02 = false; setTimeout(function(){ console.log("滾..."); mytime_02 = true; //上次執行成功,下一次才可執行。 }, 200); } /**測試:二、underscore.js進行節流操作的函式**/ document.querySelector("#send2").addEventListener("click", _.debounce(function() { console.log("點選2"); },1000)); }; </script>
<body style="height:800px;">
<div style="width:200px;height:50px;background:#333" id="send">點我</div>
<div style="width:200px;height:50px;background:#ccc" id="send2">點我2</div>
</body>
從上面我們可以進行簡單的限流,但是存在一個問題,我們這個程式碼和事件函式嚴重耦合在一次,每個事件有節流需要,就需要在方法內固定的新增那幾句程式碼,我可以慚愧的告訴你,我做的一個專案,裡面節流基本全是這樣手工一個一個加上去的。
那麼市面上有沒有比較成熟的庫中有這二個方法呢? underscore.js 就有現成的這二個方法。上面的測試二,就是呼叫此js的節流。
三、underscore.js 的節流方法的原始碼閱讀。
<!-- 二、underscore.js進行節流操作的函式 -->
<script type="text/javascript">
var _ = {};
_.now = function (){return new Date().getTime()};
/**防抖: func 執行的方法,wait 等待時間 ,immediate true表示立即執行(此處邏輯程式碼已經刪除)**/
_.debounce = function(func, wait, immediate){
var timeout, args, context, timestamp, result; //這些變數和方法引數是所有執行事件的函式共享!!!
var later = function(){
//下面三行程式碼是核心。
var last = _.now() - timestamp; //timestamp當事件觸發就會被改變,last預期就不等於wait.
if (last < wait && last >= 0) { //timestamp被改變了,照成last不夠wait,就繼續setTimeout(...,最後次事件剩餘的wait)
timeout = setTimeout(later, wait - last); //wait-last到期後 --> 等價於 timestamp + wait 的時間。
}else{
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
}
};
return function(){ /**觸發事件返回閉包函式,可以操作func,wait,以及設定的幾個全域性變數和later方法**/
context = this; //事件呼叫的話,this就是dom物件
args = arguments; //事件呼叫的話,就是事件的e引數
timestamp = _.now(); //最後次事件觸發的時間戳。
if (!timeout) //只有timeout沒有初始化的時候,才會設定setTimeout。
timeout = setTimeout(later, wait); //啟動延遲執行,later內部會遞迴呼叫。
return result;
};
};
/**節流: func 執行的方法,wait 等待時間 **/
_.throttle = function(func, wait, options){
var context, args, result;
var timeout = null;
var previous = 0;
if (!options)
options = {};
var later = function() {
previous = options.leading === false ? 0 : _.now();
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};
return function() {
var now = _.now();
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait){
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func.apply(context, args);
if (!timeout)
context = args = null;
} else if (!timeout && options.trailing !== false){
timeout = setTimeout(later, remaining);
}
return result;
};
};
</script>
underscore實現防抖核心思路:是利用閉包函式返回作為執行函式,共享timeout引數進行標記判斷,並且利用觸發事件會共同操作時間戳timestamp 來重新用setTimeout方式遞迴調整新的啟動時間。