最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript防抖與節(jié)流詳解

 更新時間:2025年10月19日 11:33:58   作者:旺代  
防抖是一種?優(yōu)化高頻觸發(fā)事件?的技術,其核心思想是在事件被頻繁觸發(fā)時,只有最后一次操作會被執(zhí)行,中間的觸發(fā)會被忽略,本文給大家介紹JavaScript防抖與節(jié)流知識,感興趣的朋友一起看看吧

 防抖(Debounce)

一、防抖的定義

防抖是一種 優(yōu)化高頻觸發(fā)事件 的技術,其核心思想是:在事件被頻繁觸發(fā)時,只有最后一次操作會被執(zhí)行,中間的觸發(fā)會被忽略

  • 典型場景:輸入框實時搜索、窗口大小調整、滾動事件等需要限制執(zhí)行頻率的場景。
  • 核心目標:減少不必要的計算或請求,提升性能和用戶體驗。

二、防抖的實現(xiàn)原理

防抖的底層實現(xiàn)依賴以下技術點:

  • 定時器(setTimeout 和 clearTimeout):用于控制事件觸發(fā)的延遲時間。
  • 閉包(Closure):保存定時器狀態(tài),確保多次觸發(fā)時能共享同一個定時器。
  • 函數(shù)包裝:將原始函數(shù)包裝成防抖函數(shù),返回一個新函數(shù)供事件調用。

三、防抖的代碼實現(xiàn)

以下是一個支持 立即執(zhí)行 和 延遲執(zhí)行 的通用防抖函數(shù):

function debounce(func, wait, immediate = false) {
  let timeout = null;
  // 返回包裝后的防抖函數(shù)
  return function (...args) {
    const context = this;
    // 如果定時器存在,清除之前的定時器(取消未執(zhí)行的延遲操作)
    if (timeout) clearTimeout(timeout);
    if (immediate) {
      // 立即執(zhí)行模式:首次觸發(fā)立即執(zhí)行,后續(xù)在 wait 時間內觸發(fā)則重新計時
      const callNow = !timeout;
      timeout = setTimeout(() => {
        timeout = null; // 恢復可執(zhí)行狀態(tài)
      }, wait);
      if (callNow) func.apply(context, args);
    } else {
      // 延遲執(zhí)行模式:最后一次觸發(fā)后等待 wait 時間再執(zhí)行
      timeout = setTimeout(() => {
        func.apply(context, args);
      }, wait);
    }
  };
}

四、代碼解析

  • 參數(shù)說明
    • func:需要防抖的原始函數(shù)。
    • wait:防抖等待時間(單位:毫秒)。
    • immediate:是否立即執(zhí)行(true 表示首次觸發(fā)立即執(zhí)行,后續(xù)觸發(fā)需等待)。
  • 閉包保存狀態(tài)
    • timeout 變量通過閉包保存定時器 ID,確保多次觸發(fā)共享同一狀態(tài)。
    • apply 方法的作用:
    • 確保原始函數(shù) func 的 this 指向正確(指向觸發(fā)事件的元素)。
    • 傳遞事件參數(shù)(如 event 對象)。
  • 兩種模式的區(qū)別
    • 立即執(zhí)行模式:首次觸發(fā)立即執(zhí)行函數(shù),之后在 wait 時間內再次觸發(fā)會重新計時,直到停止觸發(fā)超過 wait 時間后,才能再次立即執(zhí)行。
    • 延遲執(zhí)行模式:每次觸發(fā)都會重置計時,只有最后一次觸發(fā)后等待 wait 時間才會執(zhí)行。

五、使用示例

1. 輸入框實時搜索(延遲執(zhí)行模式)
<input type="text" id="searchInput" />
<script>
  const searchInput = document.getElementById('searchInput');
  // 原始搜索函數(shù)
  function search(query) {
    console.log('搜索關鍵詞:', query);
  }
  // 防抖處理(延遲執(zhí)行)
  const debouncedSearch = debounce(search, 500);
  // 綁定輸入事件
  searchInput.addEventListener('input', function (e) {
    debouncedSearch(e.target.value);
  });
</script>
2. 按鈕防重復點擊(立即執(zhí)行模式)
<button id="submitBtn">提交</button>
<script>
  const submitBtn = document.getElementById('submitBtn');
  // 原始提交函數(shù)
  function submitForm() {
    console.log('表單已提交');
  }
  // 防抖處理(立即執(zhí)行)
  const debouncedSubmit = debounce(submitForm, 1000, true);
  // 綁定點擊事件
  submitBtn.addEventListener('click', debouncedSubmit);
</script>

六、總結

  • 防抖的核心邏輯:通過定時器和閉包控制高頻事件的執(zhí)行時機。
  • 實現(xiàn)要點
    • 使用 setTimeout 和 clearTimeout 管理延遲。
    • 通過閉包保存定時器狀態(tài)。
    • 處理 this 指向和參數(shù)傳遞。
  • 應用場景
    • 輸入框實時搜索建議。
    • 窗口大小調整后的布局計算。
    • 防止按鈕重復提交。

節(jié)流(Throttle)

一、節(jié)流的定義

節(jié)流是一種 限制高頻觸發(fā)事件執(zhí)行頻率 的技術,其核心思想是:在事件被頻繁觸發(fā)時,固定時間間隔內只執(zhí)行一次操作,忽略中間的觸發(fā)

  • 典型場景:滾動事件、鼠標移動事件(如拖拽)、窗口大小調整、按鈕頻繁點擊等。
  • 核心目標:在保證功能正常的前提下,降低事件處理頻率,優(yōu)化性能。

二、節(jié)流的實現(xiàn)原理

節(jié)流的底層實現(xiàn)依賴以下技術點:

  • 定時器(setTimeout 和 clearTimeout)或時間戳:用于控制事件觸發(fā)的間隔時間。
  • 閉包(Closure):保存計時器狀態(tài),確保多次觸發(fā)時能共享同一狀態(tài)。
  • 函數(shù)包裝:將原始函數(shù)包裝成節(jié)流函數(shù),返回一個新函數(shù)供事件調用。

三、節(jié)流的代碼實現(xiàn)

以下是兩種常見的節(jié)流實現(xiàn)方式:

1. 時間戳方式(立即執(zhí)行)

首次觸發(fā)立即執(zhí)行,之后在固定間隔內忽略后續(xù)觸發(fā)。

function throttle(func, wait) {
  let previous = 0; // 上次執(zhí)行時間戳
  return function (...args) {
    const now = Date.now();
    const context = this;
    if (now - previous > wait) {
      func.apply(context, args);
      previous = now; // 更新執(zhí)行時間戳
    }
  };
}
2. 定時器方式(延遲執(zhí)行)

首次觸發(fā)后等待固定時間執(zhí)行,之后在固定間隔內忽略后續(xù)觸發(fā)。

function throttle(func, wait) {
  let timeout = null;
  return function (...args) {
    const context = this;
    if (!timeout) {
      timeout = setTimeout(() => {
        timeout = null; // 重置定時器
        func.apply(context, args);
      }, wait);
    }
  };
}
3. 結合時間戳和定時器(首尾均執(zhí)行)

首次觸發(fā)立即執(zhí)行,最后一次觸發(fā)在間隔結束后再執(zhí)行一次。

function throttle(func, wait) {
  let previous = 0;
  let timeout = null;
  return function (...args) {
    const context = this;
    const now = Date.now();
    const remaining = wait - (now - previous);
    if (remaining <= 0) {
      // 立即執(zhí)行
      if (timeout) {
        clearTimeout(timeout);
        timeout = null;
      }
      func.apply(context, args);
      previous = now;
    } else if (!timeout) {
      // 設置最后一次執(zhí)行的定時器
      timeout = setTimeout(() => {
        func.apply(context, args);
        timeout = null;
        previous = Date.now();
      }, remaining);
    }
  };
}

四、代碼解析

  • 參數(shù)說明
    • func:需要節(jié)流的原始函數(shù)。
    • wait:節(jié)流間隔時間(單位:毫秒)。
  • 閉包保存狀態(tài)
    • previous(時間戳方式)或 timeout(定時器方式)通過閉包保存狀態(tài),確保多次觸發(fā)共享同一計時器。
  • apply 方法的作用:
    • 確保原始函數(shù) func 的 this 指向正確(如事件觸發(fā)的元素)。
    • 傳遞事件參數(shù)(如 event 對象)。
  • 不同實現(xiàn)方式的區(qū)別
    • 時間戳方式:立即響應首次觸發(fā),適合需要即時反饋的場景(如按鈕點擊)。
    • 定時器方式:延遲響應首次觸發(fā),適合連續(xù)觸發(fā)但不需要立即執(zhí)行的場景(如滾動事件)。
    • 結合方式:兼顧首尾執(zhí)行,適用于需要更平滑響應的場景(如動畫)。

五、使用示例

1. 滾動事件節(jié)流(時間戳方式)
<div id="scrollArea" style="height: 2000px;"></div>
<script>
  const scrollArea = document.getElementById('scrollArea');
  // 原始滾動處理函數(shù)
  function handleScroll() {
    console.log('滾動位置:', window.scrollY);
  }
  // 節(jié)流處理(時間戳方式)
  const throttledScroll = throttle(handleScroll, 200);
  // 綁定滾動事件
  window.addEventListener('scroll', throttledScroll);
</script>
2. 按鈕防重復點擊(定時器方式)
<button id="clickBtn">點擊</button>
<script>
  const clickBtn = document.getElementById('clickBtn');
  // 原始點擊處理函數(shù)
  function handleClick() {
    console.log('按鈕點擊');
  }
  // 節(jié)流處理(定時器方式)
  const throttledClick = throttle(handleClick, 1000);
  // 綁定點擊事件
  clickBtn.addEventListener('click', throttledClick);
</script>

六、總結

  • 節(jié)流的核心邏輯:通過時間戳或定時器控制高頻事件的執(zhí)行頻率。
  • 實現(xiàn)要點
    • 使用 Date.now() 或 setTimeout 管理間隔時間。
    • 通過閉包保存計時器或時間戳狀態(tài)。
    • 處理 this 指向和參數(shù)傳遞。
  • 應用場景
  • 滾動事件觸發(fā)加載更多內容。
  • 鼠標移動時更新元素位置(如拖拽)。
  • 防止按鈕頻繁點擊導致的重復提交。

對比:防抖 vs 節(jié)流

1. 防抖(debounce)

所謂防抖,就是指觸發(fā)事件后在 n 秒內函數(shù)只能執(zhí)行一次,如果在 n 秒內又觸發(fā)了事件,則會重新計算函數(shù)執(zhí)行時間

//防抖簡單寫法
function debounce(func, t) {
  let timer = null
  return function () {
    if (timer) {
      clearTimeout(timer)
    }
    timer = setTimeout(() => func(), t)
  }
}

2. 節(jié)流(throttle)

所謂節(jié)流,就是指連續(xù)觸發(fā)事件但是在 n 秒中只執(zhí)行一次函數(shù)

//節(jié)流簡單寫法
function throttle(func, t) {
  let timer = null
  return function () {
    if (!timer) {
      timer = setTimeout(() => {
        func()
        timer = null
      }, t)
    }
  }
}

3. 對比:

特性防抖(Debounce)節(jié)流(Throttle)
觸發(fā)頻率最后一次觸發(fā)后等待 wait 時間執(zhí)行固定時間間隔內最多執(zhí)行一次
適用場景輸入框搜索、窗口大小調整滾動事件、鼠標移動事件、頻繁點擊按鈕
核心目標確保高頻觸發(fā)時只執(zhí)行一次確保高頻觸發(fā)時按固定頻率執(zhí)行

源碼解析

一、_.throttle()源碼解析

1. 核心邏輯

節(jié)流函數(shù)確保在 wait 時間間隔內最多執(zhí)行一次 func,支持首次(leading)和末次(trailing)執(zhí)行控制。

2. 關鍵源碼步驟
_.throttle(func, [wait=0], [options={}])
function throttle(func, wait, options) {
  let leading = true;
  let trailing = true;
  // 參數(shù)處理
  if (typeof options === 'object') {
    leading = 'leading' in options ? !!options.leading : leading;
    trailing = 'trailing' in options ? !!options.trailing : trailing;
  }
  let lastArgs, lastThis, result;
  let timeout = null;
  let previous = 0;
  const throttled = function(...args) {
    const now = Date.now();
    // 首次調用且不執(zhí)行 leading 時,設置 previous 為 now
    if (!previous && leading === false) previous = now;
    // 計算剩余時間
    const remaining = wait - (now - previous);
    // 需要執(zhí)行(剩余時間 <=0 或系統(tǒng)時間被修改)
    if (remaining <= 0 || remaining > wait) {
      if (timeout) {
        clearTimeout(timeout);
        timeout = null;
      }
      previous = now;
      result = func.apply(this, args);
    } else if (!timeout && trailing !== false) {
      // 設置尾調用的定時器
      timeout = setTimeout(() => {
        previous = leading === false ? 0 : Date.now();
        timeout = null;
        result = func.apply(lastThis, lastArgs);
      }, remaining);
    }
    return result;
  };
  // 提供取消方法
  throttled.cancel = function() {
    clearTimeout(timeout);
    previous = 0;
    timeout = lastArgs = lastThis = null;
  };
  return throttled;
}
3. 核心特性
  • 時間戳與定時器結合:既保證首次觸發(fā)立即響應(leading),又在停止觸發(fā)后執(zhí)行最后一次(trailing)。
  • 系統(tǒng)時間篡改兼容:檢測到 remaining > wait 時強制觸發(fā)。
  • 取消機制:允許手動取消未執(zhí)行的尾調用。

二、_.debounce()源碼解析

1. 核心邏輯

防抖函數(shù)在連續(xù)觸發(fā)時,僅在最后一次觸發(fā)后等待 wait 時間執(zhí)行一次 func,支持立即執(zhí)行模式(leading)。

2. 關鍵源碼步驟
_.debounce(func, [wait=0], [options={}])
function debounce(func, wait, options) {
  let lastArgs, lastThis, result;
  let timerId = null;
  let lastCallTime = 0;
  let leading = false;
  let maxing = false;
  let maxWait;
  // 參數(shù)處理
  if (typeof options === 'object') {
    leading = !!options.leading;
    maxing = 'maxWait' in options;
    maxWait = maxing ? Math.max(options.maxWait || 0, wait) : maxWait;
  }
  const invokeFunc = (time) => {
    const args = lastArgs;
    const thisArg = lastThis;
    lastArgs = lastThis = undefined;
    result = func.apply(thisArg, args);
    return result;
  };
  const leadingEdge = (time) => {
    // 記錄最后一次調用時間
    lastCallTime = time;
    // 設置定時器
    timerId = setTimeout(timerExpired, wait);
    // 立即執(zhí)行模式
    return leading ? invokeFunc(time) : result;
  };
  const shouldInvoke = (time) => {
    // 判斷是否需要執(zhí)行(超過 wait 或 maxWait)
    const timeSinceLastCall = time - lastCallTime;
    return (lastCallTime === 0) || (timeSinceLastCall >= wait) || 
           (maxing && timeSinceLastCall >= maxWait);
  };
  const debounced = function(...args) {
    const time = Date.now();
    lastArgs = args;
    lastThis = this;
    // 判斷是否應該執(zhí)行
    const isInvoking = shouldInvoke(time);
    if (isInvoking) {
      // 清除已有定時器
      if (timerId === null) {
        return leadingEdge(time);
      }
      // 處理 maxWait 場景
      if (maxing) {
        timerId = setTimeout(timerExpired, wait);
        return invokeFunc(time);
      }
    }
    // 設置/重置定時器
    if (timerId === null) {
      timerId = setTimeout(timerExpired, wait);
    }
    return result;
  };
  // 提供取消和立即執(zhí)行方法
  debounced.cancel = function() { /* ... */ };
  debounced.flush = function() { /* ... */ };
  return debounced;
}
3. 核心特性
  • maxWait 支持:確保在超時后強制執(zhí)行,避免長期不觸發(fā)導致的延遲。
  • 立即執(zhí)行模式:leading 選項允許首次觸發(fā)立即執(zhí)行。
  • 靈活控制:cancel 和 flush 方法提供外部控制能力。

到此這篇關于JavaScript防抖與節(jié)流的文章就介紹到這了,更多相關js 防抖與節(jié)流內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • JavaScript 處理Iframe自適應高度(同或不同域名下)

    JavaScript 處理Iframe自適應高度(同或不同域名下)

    Iframe自適應高度一直都備受關注,接下來為大家介紹下同域名下Iframe自適應高度的處理以及跨域時Iframe高度自適應,感興趣的朋友可以參考下哈
    2013-03-03
  • Bootstrap table使用方法記錄

    Bootstrap table使用方法記錄

    這篇文章主要為大家詳細介紹了Bootstrap table的使用方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • js判斷瀏覽器版本以及瀏覽器內核的方法

    js判斷瀏覽器版本以及瀏覽器內核的方法

    這篇文章主要介紹了js判斷瀏覽器版本以及瀏覽器內核的方法,可實現(xiàn)針對各個瀏覽器的判斷,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-01-01
  • 解析原來瀏覽器原生支持JS Base64編碼解碼

    解析原來瀏覽器原生支持JS Base64編碼解碼

    這篇文章主要介紹了解析原來瀏覽器原生支持JS Base64編碼解碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-08-08
  • JavaScript中將一個值轉換為字符串的方法分析[譯]

    JavaScript中將一個值轉換為字符串的方法分析[譯]

    在JavaScript中,主要有三種方法能讓任意值轉換為字符串.本文講解了每種方法以及各自的優(yōu)缺點
    2012-09-09
  • js實現(xiàn)彈窗效果

    js實現(xiàn)彈窗效果

    這篇文章主要為大家詳細介紹了js實現(xiàn)彈窗效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-08-08
  • JS深度拷貝Object Array實例分析

    JS深度拷貝Object Array實例分析

    這篇文章主要介紹了JS深度拷貝Object Array,結合實例形式分析了JavaScript深度拷貝的原理,需要的朋友可以參考下
    2016-03-03
  • 淺談JavaScript中指針和地址

    淺談JavaScript中指針和地址

    Javascript是一門基于對象的動態(tài)語言,也就是說,所有東西都是對象,一個很典型的例子就是函數(shù)也被視為普通的對象。Javascript可以通過一定的設計模式來實現(xiàn)面向對象的編程,其中this指針就是實現(xiàn)面向對象的一個很重要的特性。
    2015-07-07
  • JS實現(xiàn)鼠標單擊與雙擊事件共存

    JS實現(xiàn)鼠標單擊與雙擊事件共存

    本篇文章主要是對JS實現(xiàn)鼠標單擊與雙擊事件共存的簡單實例進行了介紹,需要的朋友可以過來參考下,希望對大家有所幫助
    2014-03-03
  • ECharts兩種動畫效果完整代碼

    ECharts兩種動畫效果完整代碼

    這篇文章主要給大家介紹了關于ECharts兩種動畫效果的相關資料,在做項目時當我們用到echarts圖表時,大部分產(chǎn)品經(jīng)理都會要求我們給圖表加上一些動畫效果,讓頁面看起來更加炫酷,需要的朋友可以參考下
    2023-07-07

最新評論

赤城县| 大理市| 犍为县| 丽水市| 潼关县| 称多县| 定日县| 凉山| 唐山市| 依兰县| 珲春市| 茶陵县| 黑水县| 通海县| 全州县| 确山县| 隆子县| 淮滨县| 谷城县| 清流县| 威海市| 永吉县| 邢台市| 祁连县| 马尔康县| 台前县| 友谊县| 葵青区| 泸溪县| 闵行区| 玉溪市| 乐清市| 岳普湖县| 乐昌市| 馆陶县| 海丰县| 津南区| 成都市| 虎林市| 夹江县| 桃园县|