在實(shí)際應(yīng)用中,因頻繁執(zhí)行DOM操作、資源加載等重行為严肪,導(dǎo)致UI停頓甚至瀏覽器崩潰。比如:
- window對象的resize谦屑、scroll事件
- 拖拽時(shí)的mousemove事件
- 射擊游戲中的mousedown驳糯、keydown事件
- 文字輸入、自動(dòng)完成的keyup事件
一個(gè)簡單的scroll操作:
window.onscroll = function(){
lazyload();
};
function lazyload(){
console.log("scroll執(zhí)行了");
}
因此我們需要借助函數(shù)節(jié)流來解決氢橙。
debounce
抖動(dòng):如果用手指一直按住一個(gè)彈簧酝枢,它將不會(huì)彈起直到你松手為止。 也就是說當(dāng)調(diào)用動(dòng)作n毫秒后悍手,才會(huì)執(zhí)行該動(dòng)作帘睦,若在這n毫秒內(nèi)又調(diào)用此動(dòng)作則將重新計(jì)算執(zhí)行時(shí)間。這種比較適合window的resize事件坦康,實(shí)際需求大多為停止改變大小n毫秒后執(zhí)行后續(xù)處理竣付;而其他事件大多的需求是以一定的頻率執(zhí)行后續(xù)處理。針對這兩種需求就出現(xiàn)了和throttle兩種解決辦法涝焙。
方法1:
window.onscroll = function(){
//lazyload();
debounce(lazyload,window);
};
function debounce(method,context){
clearTimeout(method.timeout);
method.timeout = setTimeout(function(){
method.call(context);
},500);
}
function lazyload(){
console.log("scroll執(zhí)行了"+scrollnum);
}
效果:
利用定時(shí)器卑笨,讓函數(shù)執(zhí)行延遲500毫秒,在500毫秒內(nèi)如果有函數(shù)又被調(diào)用則刪除上一次調(diào)用仑撞,這次調(diào)用500毫秒后執(zhí)行赤兴,如此往復(fù)。
方法2:
還有一種節(jié)流方式隧哮,是通過返回閉包的形式桶良,可以設(shè)置延遲時(shí)間,兩者運(yùn)行的結(jié)果是一樣沮翔,但是我在實(shí)際操作的時(shí)候設(shè)置延遲500時(shí)陨帆,滾動(dòng)過了一會(huì)才執(zhí)行了,設(shè)置為delay為100的時(shí)候在視覺上就沒有感覺延遲。而且函數(shù)也只滾動(dòng)了一次。
function debounce1(method,delay){
var timer = null;
return function(){
var context = this,args = arguments;
clearTimeout(timer);
timer = setTimeout(function(){
method.apply(context,args);
},delay);
}
}
throttle
當(dāng)我一直滾動(dòng)鼠標(biāo)的時(shí)候疲牵,lazyload函數(shù)就會(huì)不斷被延遲承二,這樣只有停下來的時(shí)候才會(huì)執(zhí)行,那么再有些需要及時(shí)顯示的情況下纲爸,就顯得不那么友好了(對于實(shí)現(xiàn)keyup事件的提示也沒有意義了)亥鸠,所以可以為函數(shù)添加一個(gè)參數(shù)作為到固定間隔必須執(zhí)行,到了這個(gè)時(shí)間間隔就必須執(zhí)行识啦,這個(gè)時(shí)候就引入了節(jié)流:
節(jié)流:如果將水龍頭擰緊直到水是以水滴的形式流出负蚊,那你會(huì)發(fā)現(xiàn)每隔一段時(shí)間,就會(huì)有一滴水流出颓哮。也就是會(huì)說預(yù)先設(shè)定一個(gè)執(zhí)行周期家妆,當(dāng)調(diào)用動(dòng)作的時(shí)刻大于等于執(zhí)行周期則執(zhí)行該動(dòng)作,然后進(jìn)入下一個(gè)新周期
代碼如下:
function throttle2(method, delay, time) {
var timeout,startTime = new Date();
return function() {
var context = this,
args = arguments,
curTime = new Date();
clearTimeout(timeout);
// 如果達(dá)到了規(guī)定的觸發(fā)時(shí)間間隔冕茅,觸發(fā) handler
if (curTime - startTime >= time) {
method.apply(context, args);
startTime = curTime;
// 沒達(dá)到觸發(fā)間隔伤极,重新設(shè)定定時(shí)器
} else {
timeout = setTimeout(method, delay);
}
};
在這個(gè)函數(shù)中,當(dāng)一次時(shí)間較長的時(shí)候還是會(huì)執(zhí)行兩次嵌赠,而不是等滾動(dòng)停止之后再執(zhí)行塑荒。達(dá)到了想要的效果,既沒有頻繁的執(zhí)行也沒有最后執(zhí)行姜挺。