何爲throttle, 何爲debounce?javascript
谷歌翻譯給出的意思:throttle 掐死??? debounce 去抖 css
好吧,按理解咱們習慣翻譯成 ——節流。java
那麼在什麼場景下須要用到?jquery
場景一:onresize,onscroll,onmousemovegit
場景二:input,autocompletegithub
若是咱們什麼都不作,瀏覽器將會頻繁的調用你綁定的事件,若是電腦配置低就會感受卡滯,也就是你的應用性能太差。瀏覽器
入門級寫法:——拖動就fire註冊的事件app
function onResize(){
console.log('log');
};
window.addEventListener('resize',onResize,false)dom
進階級寫法:——等你安靜了我再調用函數
var timer = null; function onResize(){ clearTimeout(timer); timer = setTimeout(function(){ console.log('log'); },200); }; window.addEventListener('resize',onResize,false)
這裏有個問題:引入了變量timer,它其實屬於onResize的,可是你不能放到onResie裏面。這樣寫實在是太醜了,得想辦法。
若是咱們把這個變量註冊爲onResize的屬性,變成:
function onResize(){ clearTimeout(onResize.timer); //this is difference onResize.timer = setTimeout(function(){ console.log('log'); },200); }; window.addEventListener('resize',onResize,false)
插句:這種方法在proxy中用處比較大。你們都知道要移除註冊給dom的事件,須要指向一致,這個proxy就能夠派上用場了。i.e,僅僅是個栗子!
Contextmenu.prototype = { proxy: function(fn){ if(!fn._contextmenu_proxy){ fn._contextmenu_proxy = $.proxy(fn, this); } return fn._contextmenu_proxy; }, init:function(dialModel,dialView){ var me = this; $document.on('contextmenu',me.proxy(me.onContextMenu)); } }
再進階級寫法:——封裝
網上漫天叫囂着「面向對象,封裝,繼承……」 你要不封裝一個,還面向過程 是否是會噴死?
上面咱們其實已經實現了基礎的函數節流。但咱們不能每次都要寫一樣的邏輯吧。因此抽象一個公用函數來作這件事,具體能夠這樣實現。
jquery是這樣作的,google 能搜到$.throttle,可是爲啥我在源碼裏沒搜到???
underscore.js是這樣實現的:
_.throttle = function(func, wait, options) { var context, args, result; var timeout = null; var previous = 0; options || (options = {}); var later = function() { previous = options.leading === false ? 0 : new Date; timeout = null; result = func.apply(context, args); }; return function() { var now = new Date; if (!previous && options.leading === false) previous = now; var remaining = wait - (now - previous); context = this; args = arguments; if (remaining <= 0) { clearTimeout(timeout); timeout = null; previous = now; result = func.apply(context, args); } else if (!timeout && options.trailing !== false) { timeout = setTimeout(later, remaining); } return result; }; }; // Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
_.debounce = function(func, wait, immediate) { var timeout, args, context, timestamp, result; return function() { context = this; args = arguments; timestamp = new Date(); var later = function() { var last = (new Date()) - timestamp; if (last < wait) { timeout = setTimeout(later, wait - last); } else { timeout = null; if (!immediate) result = func.apply(context, args); } }; var callNow = immediate && !timeout; if (!timeout) { timeout = setTimeout(later, wait); } if (callNow) result = func.apply(context, args); return result; }; };
調用:
function updatePosition(){ //console.log('s');
};
//默認當即執行第一次,若是不想要當即執行,能夠這樣 _.throttle(updatePosition, 500,{leading:false});
//默認當你中止操做時會延時執行最後一次,若是不想要,能夠這樣 _.throttle(updatePosition, 500,{trailing:false});
var throttled = _.throttle(updatePosition, 500);
$(window).scroll(throttled);
//debonce——debonce的意思,打個比方:你乘電梯常常碰到快要關門時有人要上,電梯得再次開了讓人上來。這個過程可能反覆好幾回,若是咱們一致等着,等到一個時間間隔沒人時再關門,是否是方便很//多,不太形象,大概也能明白什麼意思
function calculateLayout(){
console.log('resize');
};
//若是想要當即執行,傳入參數true _.debounce(calculateLayout, 2000,true);
var lazyLayout = _.debounce(calculateLayout, 2000);
$(window).resize(lazyLayout);
baidu Tangram
貌似沒看到??
好吧,咱們來實現一個簡單的封裝,不須要那麼複雜,好理解的版本。
/* *author@http://wuya1234.github.io
*思路: 1.以前已經說過,能夠用setTimeout延時處理太頻繁的操做 2.可是咱們不想讓操做過程一直沒有反應,因此須要指定好比:每隔200毫秒無論怎樣都執行一次 */
function throttle(fn,delay,mustRun){ var timer, t_start, previous; return function(){ var arg = arguments, scope = this; var now = +new Date; var defer = function(){ previous = now; fn.call(scope,arg); }; var diff = now - previous - delay; if(diff <= 0){ clearTimeout(timer); timeout = timer; previous = now; fn.call(scope,arg); }else{ timer = setTimeout(fn.call(scope,arg),diff); } }; }; function onResize(){ clearTimeout(onResize.timer); onResize.timer = setTimeout(function(){ console.log('log'); },200); }; window.addEventListener('resize',throttle(onResize),false);
更多相關知識能夠到這些地方看看:
1.http://remysharp.com/2010/07/21/throttling-function-calls/