一文搞懂JS系列(五)之閉包應(yīng)用-防抖掀鹅,節(jié)流

寫在最前面:這是我寫的一個一文搞懂JS系列專題散休。文章清晰易懂,會將會將關(guān)聯(lián)的只是串聯(lián)在一起乐尊,形成自己獨(dú)立的知識脈絡(luò)戚丸,整個合集讀完相信你也一定會有所收獲。寫作不易扔嵌,希望您能給我點(diǎn)個贊限府!

合集地址:一文搞懂JS系列專題

概覽

  • 食用時間: 10-15分鐘

  • 難度: 簡單,別跑痢缎,看完再走

  • 食用價值: JS性能優(yōu)化

  • 食材

先來看一段代碼胁勺,這會是一個貫穿全文的案例,代碼如下:

 <div id="content" style="height:150px;line-height:150px;
 text-align:center; color: #fff;background-color:black;
 font-size:80px;"></div>
 
 <script>
   let num = 1;
   const content = document.getElementById('content');
   function count() {
     content.innerHTML = num++;
   };
   content.onmousemove = count;
 </script>

可以看到独旷,在黑色色塊中移動的同時姻几, addCount 函數(shù)被瘋狂執(zhí)行,但是很多時候势告,我們不希望這個函數(shù)執(zhí)行地如此頻繁,畢竟會影響程序或者網(wǎng)頁的性能抚恒,作為 性能優(yōu)化 方案的一種咱台,接下來,我們來引入今天的主角俭驮,防抖節(jié)流

image

防抖

定義

將多次執(zhí)行變?yōu)樽詈笠淮螆?zhí)行或立即執(zhí)行回溺,你可以理解為防止手抖

使用場景

  • 搜索框搜索輸入。只需用戶最后一次輸入完混萝,再發(fā)送請求
  • 手機(jī)號遗遵、郵箱驗(yàn)證輸入檢測
  • 窗口大小Resize。只需窗口調(diào)整完成后逸嘀,計(jì)算窗口大小车要。防止重復(fù)渲染

實(shí)現(xiàn)方式

  • 非立即執(zhí)行版

這應(yīng)該是最基礎(chǔ)也是最常用的一個版本,先來看下代碼

function debounce(func,wait,...args) {
 let timeout;    //延時器變量
 return function () {
   const context = this;    //改變this指向
   if (timeout) clearTimeout(timeout);   //先判斷有沒有延時器崭倘,有則清空翼岁,畢竟要最后一次執(zhí)行
   timeout = setTimeout(() => {      
     func.apply(context, args)     //apply調(diào)用傳入方法
   }, wait);
 }
}

可以看到类垫,方法 debounce() 有兩個入?yún)ⅲ粋€方法名 func 琅坡, 以及一個延時時間 wait 悉患,單位 ms ,還有一個使用了擴(kuò)展運(yùn)算符 ... 的函數(shù)執(zhí)行時候的入?yún)?args (選傳)

接下來榆俺,使用 content.onmousemove = debounce(count,1000); 調(diào)用我們新寫的非立即執(zhí)行版的防抖售躁,先來看下實(shí)際的運(yùn)行效果,可以看到事件觸發(fā)了以后,只有在觸發(fā)以后的1s內(nèi)不再觸發(fā)茴晋,才會執(zhí)行相應(yīng)的方法陪捷,也就是 count++ 。如果停止時間間隔小于 wait 的值并且再次觸發(fā)晃跺,那么將重新計(jì)算執(zhí)行時間揩局,計(jì)時器結(jié)束以后,再執(zhí)行方法掀虎×瓒ⅲ總結(jié)而言就是觸發(fā)事件后函數(shù)不會立即執(zhí)行,而是在 n 秒后執(zhí)行烹玉,如果在 n 秒內(nèi)又觸發(fā)了事件驰怎,則會重新計(jì)算函數(shù)執(zhí)行時間,也就是方法的執(zhí)行是非立即執(zhí)行

image

整個方法的核心思想就是依靠變量 timeout 二打,用來控制當(dāng)前是否存在定時器县忌,如果有缠黍,則清空妈倔,清空完以后再繼續(xù)創(chuàng)建一個华望。所以挪鹏,在多次執(zhí)行的同時据过,不斷清空再新建卢鹦,直到停止執(zhí)行以后美旧,在停止執(zhí)行以后的 wait 毫秒以后怀大,延時器就會成功生效凡简,方法就會被觸發(fā)逼友,也就是所謂的非立即執(zhí)行,畢竟秤涩,還要等待延時器的延時 wait帜乞。

  • 立即執(zhí)行版

立即執(zhí)行版就是在觸發(fā)事件后函數(shù)會立即執(zhí)行,然后 n 秒內(nèi)不觸發(fā)事件才能繼續(xù)執(zhí)行函數(shù)的效果筐眷,代碼如下:

function debounce(func,wait,...args){
 let timeout;     //延時器變量
 return function(){
   const context = this;
   if (timeout) clearTimeout(timeout);
   let callNow = !timeout;    //是否立即執(zhí)行
   timeout = setTimeout(() => {
     timeout = null;
   },wait)
   if(callNow) func.apply(context,args)
 }
}

可以看到的是黎烈, timeout 依然是延時器,主要核心控制是靠 callNow

① 在剛初始化的時候,沒有定時器怨喘,所以剛開始 callNow=!timeout 執(zhí)行完以后津畸, callNowtrue ,再設(shè)置一個延時器必怜,然后直接執(zhí)行方法肉拓,這就是所謂的立即執(zhí)行

② 第二次的時候在進(jìn)入的時候, if (timeout) 為真梳庆,將定時器進(jìn)行清空暖途,callNow=!timeout 為假,條件不成立

if(callNow) 不成立膏执,函數(shù)不執(zhí)行驻售,因?yàn)?timeout = null ,往后將不再執(zhí)行函數(shù)更米,直到延時器完成調(diào)用 timeout = null 之后再觸發(fā)事件

④ 觸發(fā)之后欺栗,timeout = nullcallNow 賦值為真征峦,if(callNow)條件再次符合迟几,完成執(zhí)行函數(shù)

關(guān)于上面有一點(diǎn), clearTimeout(timeout) 以后栏笆,console.log(timeout) 輸出為 1

不相信的可以看一下下面的代碼輸出

let timer=setTimeout(()=>{

},1000);
clearTimeout(timer);
console.log(!timer);     //false

最后类腮,讓我們再來看一下實(shí)際使用效果,可以看到的是蛉加,觸發(fā)事件后函數(shù)會立即執(zhí)行蚜枢,然后 n 秒內(nèi)不觸發(fā)事件才能繼續(xù)執(zhí)行函數(shù)的效果

image

節(jié)流

定義

將多次執(zhí)行變?yōu)槊扛粢欢螘r間執(zhí)行一次

使用場景

  • 滾動加載,加載更多或滾到底部監(jiān)聽

實(shí)現(xiàn)方式

  • 時間戳版(立即執(zhí)行版)

在持續(xù)觸發(fā)事件的過程中针饥,函數(shù)會立即執(zhí)行厂抽,并且每隔一段時間執(zhí)行一次,代碼如下:

function throttle(func, wait, ...args){
   let pre = 0;
   return function(){
       const context = this;
       let now = Date.now();
       if (now - pre >= wait){
           func.apply(context, args);
           pre = Date.now();
       }
   }
}

① 首先定義了一個只有完成函數(shù)調(diào)用才更新當(dāng)前時間的變量 pre 丁眼,然后定義了一個實(shí)時更新的當(dāng)前時間 now

② 進(jìn)入第一次計(jì)算時間間隔筷凤, now - pre >= wait 是必定成立的,所以函數(shù)會立即觸發(fā)

③ 觸發(fā)完了以后户盯,將 pre 的值進(jìn)行更新,之后饲化, now 的值會進(jìn)行實(shí)時更新

④ 直到 now - pre >= wait 的條件成立莽鸭,也就是現(xiàn)在的時間距離上次觸發(fā)的時間大于等于 wait 的等待時間,函數(shù)會再次觸發(fā)吃靠,(畢竟只要函數(shù)不觸發(fā)硫眨,pre 的值不更新,而now一直在實(shí)時更新巢块,時間長了礁阁,條件肯定會成立的)

⑤ 以此類推巧号,完成了事件一直在觸發(fā),首次立即執(zhí)行函數(shù)姥闭,之后函數(shù)只會隔一段時間執(zhí)行

分析完了代碼丹鸿,讓我們來看看實(shí)際運(yùn)行效果,果然和我們的分析如出一轍:

image
  • 延時器版(非立即執(zhí)行版)

在持續(xù)觸發(fā)事件的過程中棚品,函數(shù)不會立即執(zhí)行靠欢,并且每隔一段時間執(zhí)行一次,在停止觸發(fā)事件后铜跑,函數(shù)還會再執(zhí)行一次门怪,代碼如下:

function throttle(func, wait, ...args){
    let timeout;
    return function(){
        const context = this;
        if(!timeout){
            timeout = setTimeout(() => {
                timeout = null;
                func.apply(context,args);
            },wait)
        }
    }
}

① 首先定義了一個延時器變量 timeout ,先判斷是否有延時器锅纺,沒有則創(chuàng)建掷空,所以第一次進(jìn)入函數(shù)的時候,會先創(chuàng)建一個延時器

② 再次進(jìn)入函數(shù)的時候囤锉,因?yàn)楫?dāng)前已經(jīng)存在延時器了坦弟,所以什么都不做

③ 什么都不做直到延時器的時間結(jié)束,函數(shù)開始執(zhí)行嚼锄,timeout 進(jìn)行清空并且執(zhí)行函數(shù)

④ 清空以后减拭,再一次判斷, if(!timeout) 條件成立区丑,繼續(xù)創(chuàng)建延時器

⑤ 以此類推拧粪,有延時器就什么都不做,沒有了延時器則創(chuàng)建

⑥ 即使不觸發(fā)事件沧侥,延時器仍然存在可霎,所以,停止觸發(fā)事件以后宴杀,函數(shù)仍然會再執(zhí)行一次

分析完了代碼癣朗,讓我們來看看實(shí)際運(yùn)行效果,果然和我們的分析如出一轍:

image

系列目錄

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末翘骂,一起剝皮案震驚了整個濱河市壁熄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌碳竟,老刑警劉巖草丧,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異莹桅,居然都是意外死亡昌执,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門诈泼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來懂拾,“玉大人,你說我怎么就攤上這事铐达♂常” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵瓮孙,是天一觀的道長唐断。 經(jīng)常有香客問我,道長杭抠,這世上最難降的妖魔是什么脸甘? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮偏灿,結(jié)果婚禮上丹诀,老公的妹妹穿的比我還像新娘。我一直安慰自己翁垂,他們只是感情好铆遭,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著沿猜,像睡著了一般枚荣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上邢疙,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天棍弄,我揣著相機(jī)與錄音,去河邊找鬼疟游。 笑死呼畸,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的颁虐。 我是一名探鬼主播蛮原,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼另绩!你這毒婦竟也來了儒陨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤笋籽,失蹤者是張志新(化名)和其女友劉穎蹦漠,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體车海,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡笛园,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了侍芝。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片研铆。...
    茶點(diǎn)故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖州叠,靈堂內(nèi)的尸體忽然破棺而出棵红,到底是詐尸還是另有隱情,我是刑警寧澤咧栗,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布逆甜,位于F島的核電站,受9級特大地震影響楼熄,放射性物質(zhì)發(fā)生泄漏忆绰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一可岂、第九天 我趴在偏房一處隱蔽的房頂上張望错敢。 院中可真熱鬧,春花似錦缕粹、人聲如沸稚茅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽亚享。三九已至,卻和暖如春绘面,著一層夾襖步出監(jiān)牢的瞬間欺税,已是汗流浹背侈沪。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留晚凿,地道東北人亭罪。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像歼秽,于是被迫代替她去往敵國和親应役。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評論 2 353