JS專題系列之防抖與截流

一在辆、前言

在前端開發(fā)中會遇到一些頻繁的事件觸發(fā)规揪,比如:

  • window 的 resize工闺、scroll
  • mousedown乍赫、mousemove
  • keyup、keydown

為此陆蟆,我們舉個示例代碼來了解事件如何頻繁的觸發(fā):

<!DOCTYPE html>
<html lang="zh-cmn-Hans">

<head>
    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge, chrome=1">
    <title>debounce</title>
    <style>
        #container{
            width: 100%; height: 200px; line-height: 200px; text-align: center; color: #fff; background-color: #444; font-size: 30px;
        }
    </style>
</head>

<body>
    <div id="container"></div>
</body>
  <script>
    var count = 1;
    var container = document.getElementById('container');

    function getUserAction() {
      container.innerHTML = count++;
    };

    container.onmousemove = getUserAction;
  </script>
</html>

因為這個例子很簡單雷厂,所以瀏覽器完全反應的過來,可是如果是復雜的回調(diào)函數(shù)或是 ajax 請求呢叠殷?假設 1 秒觸發(fā)了 60 次改鲫,每個回調(diào)就必須在 1000 / 60 = 16.67ms 內(nèi)完成,否則就會有卡頓出現(xiàn)

為了解決這個問題,一般有兩種解決方案:

  • debounce 防抖

  • throttle 節(jié)流

二像棘、debounce原理

防抖的原理就是:你盡管觸發(fā)事件稽亏,但是我一定在事件觸發(fā) n 秒后才執(zhí)行,如果你在一個事件觸發(fā)的 n 秒內(nèi)又觸發(fā)了這個事件缕题,那我就以新的事件的時間為準截歉,n 秒后才執(zhí)行

function debounce(callback,wait=3000) {
  var timeout;
  return function() {
    clearTimeout(timeout);
    timeout = setTimeout(callback,wait);
  }
}

上述的方法看似沒有什么問題,但是參考lodash烟零、underscore你會發(fā)現(xiàn)這類庫封裝的非常完善

  • 函數(shù)中this指向
  • 事件對象
  • 函數(shù)立即執(zhí)行

根據(jù)以上的幾個問題我們進行進一步的完善

1瘪松、this指向

function debounce(callback,wait=3000) {
  var timeout;
  return function() {
    var context = this;
    clearTimeout(timeout);
    timeout = setTimeout(function(){
      callback.apply(context)
    },wait);
  }
}

這里解釋一個為什么var context = this; 要加一行這樣的代碼,難道默認不是指向window嗎锨阿?因為在JS的嚴格模式下this會指向undefined 宵睦,另外在Node環(huán)境中是沒有window對象的,其實我們這里代碼也不是特別嚴謹墅诡,后續(xù)會繼續(xù)完善

'use strict'
function debounce(callback,wait=3000) {
  var timeout;
  return function() {
    clearTimeout(timeout);
    timeout = setTimeout(function(){
      callback()
    },wait);
  }
}
function getUserAction(){
  console.log(1,this) // 1 undefined
}

document.onmousemove = debounce(getUserAction, 1000);

2壳嚎、事件對象

上述方法中是無法訪問到event對象的,因此我們需要再次完善

 'use strict'
    function debounce(callback,wait=3000) {
        var timeout;
        return function() {
            clearTimeout(timeout);
            timeout = setTimeout(function(){
                callback()
            },wait);
        }
    }
    function getUserAction(e){
        console.log(1,this,e) // 1 undefined undefined
    }

    document.onmousemove = debounce(getUserAction, 1000);

完善event對象

'use strict'
function debounce(callback,wait=3000) {
  var timeout;
  return function() {
    var context = this;
    var args = arguments;
    clearTimeout(timeout);
    timeout = setTimeout(function(){
      callback.apply(context,args);
    },wait);
  }
}

3书斜、立即執(zhí)行

什么是立即執(zhí)行诬辈?簡單的來說就是我希望第一次執(zhí)行的時候沒有時間的延遲,第二次的時候才會有時間的延遲荐吉。如果你聽不懂我說的話,那么可以告訴你就是類似于Vue的watch方法的immediate屬性口渔。默認第一次會進行監(jiān)聽一樣

  "use strict";
  function debounce(callback, wait = 3000, immediate) {
    var timeout,result
    return function () {
      var context = this;
      var args = arguments;

      if (timeout) clearTimeout(timeout);
      if (immediate) {
        // 判斷是否執(zhí)行過
        var flag = !timeout;
        timeout = setTimeout(function () {
            callback.apply(context, args);
        }, wait);
        if (flag) callback.apply(context, args);
      } else {
        timeout = setTimeout(function () {
          callback.apply(context, args);
        }, wait);
      }
    };
  }

三样屠、throttle原理

節(jié)流的原理也很簡單,假設原本1秒會執(zhí)行100次的函數(shù)缺脉,我們可以控制到1秒執(zhí)行10次痪欲。

方案:當觸發(fā)事件的時候,我們?nèi)〕霎斍暗臅r間戳攻礼,然后減去之前的時間戳(最一開始值設為 0 )业踢,如果大于設置的時間周期,就執(zhí)行函數(shù)礁扮,然后更新時間戳為當前的時間戳知举,如果小于,就不執(zhí)行

  "use strict";
  function throttle(callback, wait = 300) {
    var context,
        args,
        firstTime = 0;
    return function () {
      var iNow = +new Date();
      context = this;
      args = arguments;
      if (iNow - firstTime > wait) {
        console.log(111);
        callback.apply(context, args);
        firstTime = iNow;
      }
    };
  }

接下來我們進行優(yōu)化太伊,原因是如果我們在最后一次停止觸發(fā)的時候如果時間差沒有達到300ms那么最后一次是不執(zhí)行的雇锡,因此我們需要結(jié)合定時器來進行優(yōu)化

  "use strict";
  function throttle(callback, delay, immediate=true) {
    var timer,context,iNow,firstTime = +new Date(),args = [];
    return function() {
      clearTimeout(timer);
      context = this;
      iNow = +new Date();
      args = Array.prototype.slice.call(arguments);
      // 判斷是否是第一次執(zhí)行
      if(immediate) {
        immediate = false;
        callback.apply(context,args);
      } else {
        // 第二次執(zhí)行的時候判斷時間差
        if(iNow - firstTime > delay) {
          firstTime = iNow;
          callback.apply(context,args);
        } else {
          // 判斷是否是最后一次執(zhí)行
          timer = setTimeout(function(){
            callback.apply(context,args);
          },delay)
        }
      }
    }
  }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市僚焦,隨后出現(xiàn)的幾起案子锰提,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件立肘,死亡現(xiàn)場離奇詭異边坤,居然都是意外死亡,警方通過查閱死者的電腦和手機谅年,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門茧痒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人踢故,你說我怎么就攤上這事文黎。” “怎么了殿较?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵耸峭,是天一觀的道長。 經(jīng)常有香客問我淋纲,道長劳闹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任洽瞬,我火速辦了婚禮本涕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘伙窃。我一直安慰自己菩颖,他們只是感情好,可當我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布为障。 她就那樣靜靜地躺著晦闰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鳍怨。 梳的紋絲不亂的頭發(fā)上呻右,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天,我揣著相機與錄音鞋喇,去河邊找鬼声滥。 笑死,一個胖子當著我的面吹牛侦香,可吹牛的內(nèi)容都是我干的落塑。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼鄙皇,長吁一口氣:“原來是場噩夢啊……” “哼芜赌!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起伴逸,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤缠沈,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體洲愤,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡颓芭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了柬赐。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片亡问。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖肛宋,靈堂內(nèi)的尸體忽然破棺而出州藕,到底是詐尸還是另有隱情,我是刑警寧澤酝陈,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布床玻,位于F島的核電站,受9級特大地震影響沉帮,放射性物質(zhì)發(fā)生泄漏锈死。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一穆壕、第九天 我趴在偏房一處隱蔽的房頂上張望待牵。 院中可真熱鬧,春花似錦喇勋、人聲如沸缨该。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽压彭。三九已至,卻和暖如春渗常,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背汗盘。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工皱碘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人隐孽。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓癌椿,卻偏偏與公主長得像,于是被迫代替她去往敵國和親菱阵。 傳聞我的和親對象是個殘疾皇子踢俄,可洞房花燭夜當晚...
    茶點故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 閉包 由于節(jié)流和防抖函數(shù)的實現(xiàn)都用到了閉包,所以在了解節(jié)流和防抖之前我先簡單介紹下什么是閉包晴及。 由于js代碼在一個...
    _章魚小丸子閱讀 772評論 0 0
  • 防抖(debounce):在指定的時間 n 秒后執(zhí)行回調(diào)都办,如果在計算時間的過程中又被觸發(fā),則從新開始計算時間 fu...
    sharon_007閱讀 759評論 0 0
  • 前言 在前端開發(fā)中會遇到一些頻繁的事件觸發(fā)势木,比如: window 的 resize、scroll mousedow...
    peng233閱讀 156評論 0 0
  • 在前端開發(fā)中歌懒,經(jīng)常會遇到頻繁觸發(fā)某一事件的情況啦桌,如 scroll、mousemove及皂、onchange等甫男。這種高頻...
    vinoooooo閱讀 336評論 0 0
  • 久違的晴天,家長會验烧。 家長大會開好到教室時板驳,離放學已經(jīng)沒多少時間了。班主任說已經(jīng)安排了三個家長分享經(jīng)驗噪窘。 放學鈴聲...
    飄雪兒5閱讀 7,523評論 16 22