欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript性能優(yōu)化之函數(shù)節(jié)流(throttle)與函數(shù)去抖(debounce)

 更新時間:2016年08月11日 11:06:01   作者:蘇服  
這篇文章主要介紹了JavaScript性能優(yōu)化之函數(shù)節(jié)流(throttle)與函數(shù)去抖(debounce)的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下

函數(shù)節(jié)流,簡單地講,就是讓一個函數(shù)無法在很短的時間間隔內(nèi)連續(xù)調(diào)用,只有當(dāng)上一次函數(shù)執(zhí)行后過了你規(guī)定的時間間隔,才能進(jìn)行下一次該函數(shù)的調(diào)用。

函數(shù)節(jié)流的原理挺簡單的,估計大家都想到了,那就是定時器。當(dāng)我觸發(fā)一個時間時,先setTimout讓這個事件延遲一會再執(zhí)行,如果在這個時間間隔內(nèi)又觸發(fā)了事件,那我們就clear掉原來的定時器,再setTimeout一個新的定時器延遲一會執(zhí)行,就這樣。

以下場景往往由于事件頻繁被觸發(fā),因而頻繁執(zhí)行DOM操作、資源加載等重行為,導(dǎo)致UI停頓甚至瀏覽器崩潰。

1. window對象的resize、scroll事件

2. 拖拽時的mousemove事件

3. 射擊游戲中的mousedown、keydown事件

4. 文字輸入、自動完成的keyup事件

實際上對于window的resize事件,實際需求大多為停止改變大小n毫秒后執(zhí)行后續(xù)處理;而其他事件大多的需求是以一定的頻率執(zhí)行后續(xù)處理。針對這兩種需求就出現(xiàn)了debounce和throttle兩種解決辦法。

throttle 和 debounce 是解決請求和響應(yīng)速度不匹配問題的兩個方案。二者的差異在于選擇不同的策略。

throttle 等時間 間隔執(zhí)行函數(shù)。

debounce 時間間隔 t 內(nèi)若再次觸發(fā)事件,則重新計時,直到停止時間大于或等于 t 才執(zhí)行函數(shù)。

一、throttle函數(shù)的簡單實現(xiàn)

function throttle(fn, threshhold, scope) { 
threshhold || (threshhold = 250); 
var last, 
timer; return function () { 
var context = scope || this; 
var now = +new Date(), 
args = arguments; 
if (last && now - last + threshhold < 0) { 
// hold on to it 
clearTimeout(deferTimer); 
timer = setTimeout(function () { 
last = now; 
fn.apply(context, args); 
}, threshhold); 
} else { 
last = now; 
fn.apply(context, args); 
} 
};}

調(diào)用方法

$('body').on('mousemove', throttle(function (event) 
{
console.log('tick');
}, 1000));

二、debounce函數(shù)的簡單實現(xiàn)

function debounce(fn, delay) 
{ 
var timer = null; 
return function () 
{ 
var context = this,
args = arguments; 
clearTimeout(timer); 
timer = setTimeout(function () { 
fn.apply(context, args); 
}, delay); 
};}

調(diào)用方法

$('input.username').keypress(debounce(function (event)
{
// do the Ajax request
}, 250));

三、簡單的封裝實現(xiàn)

/** * throttle * @param fn, wait, debounce */var throttle = function ( fn, wait, debounce ) { 
var timer = null, // 定時器 
t_last = null, // 上次設(shè)置的時間 
context, // 上下文 
args, // 參數(shù) 
diff; // 時間差 
return funciton () { 
var curr = + new Date(); 
var context = this, args = arguments; 
clearTimeout( timer ); 
if ( debounce ) { // 如果是debounce 
timer = setTimeout( function () { 
fn.apply( context, args ); 
}, wait ); 
} else { // 如果是throttle 
if ( !t_last ) t_last = curr; 
if ( curr - t_last &gt;= wait ) { 
fn.apply( context, wait ); 
context = wait = null; 
} 
} }}/** * debounce * @param fn, wait */var debounce = function ( fn, wait ) 
{ 
return throttle( fn, wait, true );
}

小結(jié):這兩個方法適用于會重復(fù)觸發(fā)的一些事件,如:mousemove,keydown,keyup,keypress,scroll等。
如果只綁定原生事件,不加以控制,會使得瀏覽器卡頓,用戶體驗差。為了提高js性能,建議在使用以上及類似事件的時候用函數(shù)節(jié)流或者函數(shù)去抖加以控制。

四、underscore v1.7.0相關(guān)的源碼剖析                          

1. _.throttle函數(shù)

_.throttle = function(func, wait, options) { 
var context, args, result; 
var timeout = null; 
// 定時器 
var previous = 0; 
// 上次觸發(fā)的時間 
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(); 
// 第一次是否執(zhí)行 
if (!previous &amp;&amp; options.leading === false) previous = now; 
// 這里引入了一個remaining的概念:還剩多長時間執(zhí)行事件 
var remaining = wait - (now - previous); 
context = this; 
args = arguments; 
// remaining &lt;= 0 考慮到事件停止后重新觸發(fā)或者 
// 正好相差wait的時候,這些情況下,會立即觸發(fā)事件 
// remaining &gt; wait 沒有考慮到相應(yīng)場景 
// 因為now-previous永遠(yuǎn)都是正值,且不為0,那么 
// remaining就會一直比wait小,沒有大于wait的情況 
// 估計是保險起見吧,這種情況也是立即執(zhí)行 
if (remaining &lt;= 0 || remaining &gt; wait) 
{ 
if (timeout)
{ 
clearTimeout(timeout); 
timeout = null; 
} 
previous = now; 
result = func.apply(context, args); 
if (!timeout) context = args = null; 
// 是否跟蹤 
} else if (!timeout &amp;&amp; options.trailing !== false)
{ 
timeout = setTimeout(later, remaining); 
} 
return result; 
};};

由上可見,underscore考慮了比較多的情況:options.leading:

第一次是否執(zhí)行,默認(rèn)為true,表示第一次會執(zhí)行,傳入{leading:false}則禁用第一次執(zhí)行options.trailing:最后一次是否執(zhí)行,默認(rèn)為true,表示最后一次會執(zhí)行,傳入{trailing: false}表示最后一次不執(zhí)行所謂第一次是否執(zhí)行,是剛開始觸發(fā)事件時,要不要先觸發(fā)事件,如果要,則previous=0,remaining 為負(fù)值,則立即調(diào)用了函數(shù)所謂最后一次是否執(zhí)行,是事件結(jié)束后,最后一次觸發(fā)了此方法,如果要執(zhí)行,則設(shè)置定時器,即事件結(jié)束以后還要在執(zhí)行一次。remianing > wait 表示客戶端時間被修改過。

2. _.debounce函數(shù)

_.debounce = function(func, wait, immediate) { 
// immediate默認(rèn)為false 
var timeout, args, context, timestamp, result; 
var later = function() { 
// 當(dāng)wait指定的時間間隔期間多次調(diào)用_.debounce返回的函數(shù),則會不斷更新timestamp的值,導(dǎo)致last < wait && last >= 0一直為true,從而不斷啟動新的計時器延時執(zhí)行func var last = _.now() - timestamp; 
if (last < wait && last >= 0) { 
timeout = setTimeout(later, wait - last); 
} else { 
timeout = null; 
if (!immediate) { 
result = func.apply(context, args); 
if (!timeout) context = args = null; 
} 
} 
}; 
return function() 
{ 
context = this; 
args = arguments; 
timestamp = _.now(); 
// 第一次調(diào)用該方法時,且immediate為true,則調(diào)用func函數(shù) 
var callNow = immediate && !timeout; // 在wait指定的時間間隔內(nèi)首次調(diào)用該方法,則啟動計時器定時調(diào)用func函數(shù) 
if (!timeout) timeout = setTimeout(later, wait); 
if (callNow) { 
result = func.apply(context, args); 
context = args = null; 
} 
return result; 
};};

_.debounce實現(xiàn)的精彩之處我認(rèn)為是通過遞歸啟動計時器來代替通過調(diào)用clearTimeout來調(diào)整調(diào)用func函數(shù)的延時執(zhí)行。

以上所述是小編給大家介紹的JavaScript性能優(yōu)化之函數(shù)節(jié)流(throttle)與函數(shù)去抖(debounce),希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!

相關(guān)文章

最新評論