js的防抖和節(jié)流

在上周的開發(fā)中预鬓,又遇到點(diǎn)擊保存多次請(qǐng)求數(shù)據(jù)重復(fù)的問題濒募,所以下來學(xué)習(xí)了一下js的防抖和節(jié)流鞭盟。
通過學(xué)習(xí)了解到,在進(jìn)行窗口的resize瑰剃、scroll齿诉,輸入框內(nèi)容校驗(yàn)等操作時(shí),如果事件處理函數(shù)調(diào)用的頻率無限制晌姚,會(huì)加重瀏覽器的負(fù)擔(dān)粤剧,會(huì)導(dǎo)致數(shù)據(jù)重復(fù)等一系列問題。此時(shí)我們可以采用debounce(防抖)和throttle(節(jié)流)的方式來減少調(diào)用頻率挥唠,同時(shí)又不影響實(shí)際效果抵恋。

函數(shù)防抖(debounce)

當(dāng)持續(xù)觸發(fā)事件時(shí),一定時(shí)間段內(nèi)沒有再觸發(fā)事件宝磨,事件處理函數(shù)才會(huì)執(zhí)行一次弧关,如果設(shè)定的時(shí)間到來之前,又一次觸發(fā)了事件唤锉,就重新開始延時(shí)世囊。
簡單防抖的實(shí)現(xiàn):

// 防抖
function debounce(fn, wait) {    
    var timeout = null;    
    return function() {        
        if(timeout !== null)   clearTimeout(timeout);        
        timeout = setTimeout(fn, wait);    
    }
}
// 處理函數(shù)
function handle() {    
    console.log(Math.random()); 
}
// 滾動(dòng)事件
window.addEventListener('scroll', debounce(handle, 1000));

當(dāng)持續(xù)觸發(fā)scroll事件時(shí),事件處理函數(shù)handle只在停止?jié)L動(dòng)1000毫秒之后才會(huì)調(diào)用一次窿祥,也就是說在持續(xù)觸發(fā)scroll事件的過程中株憾,事件處理函數(shù)handle一直沒有執(zhí)行。

函數(shù)節(jié)流(throttle)

當(dāng)持續(xù)觸發(fā)事件時(shí)晒衩,保證一定時(shí)間段內(nèi)只調(diào)用一次事件處理函數(shù)嗤瞎。節(jié)流通俗解釋就比如我們水龍頭放水,閥門一打開浸遗,水嘩嘩的往下流猫胁,秉著勤儉節(jié)約的優(yōu)良傳統(tǒng)美德,我們要把水龍頭關(guān)小點(diǎn)跛锌,最好是如我們心意按照一定規(guī)律在某個(gè)時(shí)間間隔內(nèi)一滴一滴的往下滴弃秆。
函數(shù)節(jié)流主要有兩種實(shí)現(xiàn)方法:時(shí)間戳和定時(shí)器届惋。接下來分別用兩種方法實(shí)現(xiàn)throttle:

節(jié)流throttle代碼(時(shí)間戳):
var throttle = function(func, delay) {            
  var prev = Date.now();            
  return function() {                
    var context = this;                
    var args = arguments;                
    var now = Date.now();                
    if (now - prev >= delay) {                    
      func.apply(context, args);                    
      prev = Date.now();                
    }            
  }        
}        
function handle() {            
  console.log(Math.random());        
}        
window.addEventListener('scroll', throttle(handle, 1000));

當(dāng)高頻事件觸發(fā)時(shí),第一次會(huì)立即執(zhí)行(給scroll事件綁定函數(shù)與真正觸發(fā)事件的間隔一般大于delay)菠赚,而后再怎么頻繁地觸發(fā)事件脑豹,也都是每delay時(shí)間才執(zhí)行一次。而當(dāng)最后一次事件觸發(fā)完畢后衡查,事件也不會(huì)再被執(zhí)行了 瘩欺。

節(jié)流throttle代碼(定時(shí)器):
var throttle = function(func, delay) {            
    var timer = null;            
    return function() {                
        var context = this;               
        var args = arguments;                
        if (!timer) {                    
            timer = setTimeout(function() {                        
                func.apply(context, args);                        
                timer = null;                    
            }, delay);                
        }            
    }        
}        
function handle() {            
    console.log(Math.random());        
}        
window.addEventListener('scroll', throttle(handle, 1000));

當(dāng)觸發(fā)事件的時(shí)候,我們?cè)O(shè)置一個(gè)定時(shí)器拌牲,再次觸發(fā)事件的時(shí)候俱饿,如果定時(shí)器存在,就不執(zhí)行塌忽,直到delay時(shí)間后拍埠,定時(shí)器執(zhí)行執(zhí)行函數(shù),并且清空定時(shí)器土居,這樣就可以設(shè)置下個(gè)定時(shí)器枣购。當(dāng)?shù)谝淮斡|發(fā)事件時(shí),不會(huì)立即執(zhí)行函數(shù)擦耀,而是在delay秒后才執(zhí)行棉圈。而后再怎么頻繁觸發(fā)事件,也都是每delay時(shí)間才執(zhí)行一次眷蜓。當(dāng)最后一次停止觸發(fā)后分瘾,由于定時(shí)器的delay延遲,可能還會(huì)執(zhí)行一次函數(shù)账磺。

節(jié)流中用時(shí)間戳或定時(shí)器都是可以的芹敌。更精確地,可以用時(shí)間戳+定時(shí)器垮抗,當(dāng)?shù)谝淮斡|發(fā)事件時(shí)馬上執(zhí)行事件處理函數(shù)氏捞,最后一次觸發(fā)事件后也還會(huì)執(zhí)行一次事件處理函數(shù)。

節(jié)流throttle代碼(時(shí)間戳+定時(shí)器):
var throttle = function(func, delay) {     
    var timer = null;     
    var startTime = Date.now();     
    return function() {             
        var curTime = Date.now();             
        var remaining = delay - (curTime - startTime);             
        var context = this;             
        var args = arguments;             
        clearTimeout(timer);              
        if (remaining <= 0) {                    
            func.apply(context, args);                    
            startTime = Date.now();              
        } else {                    
            timer = setTimeout(func, remaining);              
        }      
    }
}
function handle() {      
    console.log(Math.random());
} 
window.addEventListener('scroll', throttle(handle, 1000));

在節(jié)流函數(shù)內(nèi)部使用開始時(shí)間startTime冒版、當(dāng)前時(shí)間curTime與delay來計(jì)算剩余時(shí)間remaining液茎,當(dāng)remaining<=0時(shí)表示該執(zhí)行事件處理函數(shù)了(保證了第一次觸發(fā)事件就能立即執(zhí)行事件處理函數(shù)和每隔delay時(shí)間執(zhí)行一次事件處理函數(shù))。如果還沒到時(shí)間的話就設(shè)定在remaining時(shí)間后再觸發(fā) (保證了最后一次觸發(fā)事件后還能再執(zhí)行一次事件處理函數(shù))辞嗡。當(dāng)然在remaining這段時(shí)間中如果又一次觸發(fā)事件捆等,那么會(huì)取消當(dāng)前的計(jì)時(shí)器,并重新計(jì)算一個(gè)remaining來判斷當(dāng)前狀態(tài)续室。

總結(jié)

函數(shù)防抖:將幾次操作合并為一此操作進(jìn)行栋烤。原理是維護(hù)一個(gè)計(jì)時(shí)器,規(guī)定在delay時(shí)間后觸發(fā)函數(shù)挺狰,但是在delay時(shí)間內(nèi)再次觸發(fā)的話明郭,就會(huì)取消之前的計(jì)時(shí)器而重新設(shè)置肺稀。這樣一來彤悔,只有最后一次操作能被觸發(fā)汇恤。

函數(shù)節(jié)流:使得一定時(shí)間內(nèi)只觸發(fā)一次函數(shù)埠帕。原理是通過判斷是否到達(dá)一定時(shí)間來觸發(fā)函數(shù)。

區(qū)別: 函數(shù)節(jié)流不管事件觸發(fā)有多頻繁话侄,都會(huì)保證在規(guī)定時(shí)間內(nèi)一定會(huì)執(zhí)行一次真正的事件處理函數(shù)亏推,而函數(shù)防抖只是在最后一次事件后才觸發(fā)一次函數(shù)。 比如在頁面的無限加載場景下年堆,我們需要用戶在滾動(dòng)頁面時(shí)吞杭,每隔一段時(shí)間發(fā)一次 Ajax 請(qǐng)求,而不是在用戶停下滾動(dòng)頁面操作時(shí)才去請(qǐng)求數(shù)據(jù)变丧。這樣的場景篇亭,就適合用節(jié)流技術(shù)來實(shí)現(xiàn)

當(dāng)然,在了解原理之后锄贷,我們看可以使用lodash-decorators工具的防抖函數(shù):Debounce
首先下載依賴: npm install lodash-decorators --save
在函數(shù)中使用裝飾器

image.png

這樣借助其他的工具就可以很好地實(shí)現(xiàn)函數(shù)的節(jié)流和防抖。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末曼月,一起剝皮案震驚了整個(gè)濱河市谊却,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌哑芹,老刑警劉巖炎辨,帶你破解...
    沈念sama閱讀 216,843評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異聪姿,居然都是意外死亡碴萧,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門末购,熙熙樓的掌柜王于貴愁眉苦臉地迎上來破喻,“玉大人,你說我怎么就攤上這事盟榴〔苤剩” “怎么了?”我有些...
    開封第一講書人閱讀 163,187評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵擎场,是天一觀的道長羽德。 經(jīng)常有香客問我,道長迅办,這世上最難降的妖魔是什么宅静? 我笑而不...
    開封第一講書人閱讀 58,264評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮站欺,結(jié)果婚禮上姨夹,老公的妹妹穿的比我還像新娘纤垂。我一直安慰自己,他們只是感情好匀伏,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評(píng)論 6 390
  • 文/花漫 我一把揭開白布洒忧。 她就那樣靜靜地躺著,像睡著了一般够颠。 火紅的嫁衣襯著肌膚如雪熙侍。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,231評(píng)論 1 299
  • 那天履磨,我揣著相機(jī)與錄音蛉抓,去河邊找鬼。 笑死剃诅,一個(gè)胖子當(dāng)著我的面吹牛巷送,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播矛辕,決...
    沈念sama閱讀 40,116評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼笑跛,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了聊品?” 一聲冷哼從身側(cè)響起飞蹂,我...
    開封第一講書人閱讀 38,945評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎翻屈,沒想到半個(gè)月后陈哑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,367評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡伸眶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評(píng)論 2 333
  • 正文 我和宋清朗相戀三年惊窖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片厘贼。...
    茶點(diǎn)故事閱讀 39,754評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡界酒,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出涂臣,到底是詐尸還是另有隱情盾计,我是刑警寧澤,帶...
    沈念sama閱讀 35,458評(píng)論 5 344
  • 正文 年R本政府宣布赁遗,位于F島的核電站署辉,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏岩四。R本人自食惡果不足惜哭尝,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望剖煌。 院中可真熱鬧材鹦,春花似錦逝淹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至尤泽,卻和暖如春欣簇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背坯约。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評(píng)論 1 269
  • 我被黑心中介騙來泰國打工熊咽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人闹丐。 一個(gè)月前我還...
    沈念sama閱讀 47,797評(píng)論 2 369
  • 正文 我出身青樓横殴,卻偏偏與公主長得像,于是被迫代替她去往敵國和親卿拴。 傳聞我的和親對(duì)象是個(gè)殘疾皇子衫仑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評(píng)論 2 354