函數節流與防抖的實現

来源:http://www.cnblogs.com/unclekeith/archive/2017/09/02/7466205.html
-Advertisement-
Play Games

函數節流與函數防抖 最近由於處於互聯網大廠的秋招季節,因此這些天都在看前端性能優化和演算法方面的知識。在性能優化方面,看了網上的一些文章,同時看完了《高性能網站建設指南》和《高性能JavaScript》兩本書,頗有收穫,可以參看這篇文章,主要是一些前端性能優化方面的總結。傳送門: "前端性能優化最佳實 ...


函數節流與函數防抖

最近由於處於互聯網大廠的秋招季節,因此這些天都在看前端性能優化和演算法方面的知識。在性能優化方面,看了網上的一些文章,同時看完了《高性能網站建設指南》和《高性能JavaScript》兩本書,頗有收穫,可以參看這篇文章,主要是一些前端性能優化方面的總結。傳送門:前端性能優化最佳實踐

這篇文章主要是講函數節流與函數防抖相關知識的。雖然在上面兩本書裡面沒有談及這兩方面的內容,但是我覺得,對JS常用事件進行節流或者防抖的處理是屬於性能優化方面的。

目的

實現了這兩個功能函數之後發現,節流同防抖在實現過程可能不太一樣,但是目的和本質都是一樣的:

需要節流或者防抖的函數通過定時器進行間隔調用,目的是只有在執行函數的請求停止了一段時間之後才執行。

防抖和節流主要運用了時間差、閉包、定時器來處理。

相同點

節流和防抖都可以採用閉包的形式和定時器間隔調用函數的方式來實現。這主要運用了閉包的一個特性:能夠記住並訪問所在的此法作用的標識符。如果對閉包不瞭解的可以看看這個回答:什麼是閉包

函數節流
function throttle () {
    var time = null
    ...
    // 閉包
    return function () {
        window.setTimeout(time)
        time = window.setTimeout(() => {...})
    }
}
函數防抖
function debounce () {
    var time = null
    // 閉包
    return function () {
        if (!time) {
            time = window.setTimeout(later, delay)
            ...
        }
    }
}
用途

個人認為,能夠使用函數節流的場景,也可以用於函數防抖。能夠使用函數防抖的場景,也適用於函數節流。

在為某個DOM節點綁定事件時,有些事件會不斷的觸發瀏覽器進行計算。如resize, mousemove, keydown, keypress, keyup, touchmove等。對於這些時間都可以使用節流或者防抖進行處理。

函數防抖

最基本的防抖實現,是沒有加入各種條件判斷的時候。當然,閱讀源碼的一個方法也是跳過各種if判斷。

function debounce (fn, option) {
    var [time, context, args, result] = []
    let setting = {
        delay: 300    // 延遲delay秒之後間隔執行函數
    }
    option = Object.assign({}, setting, option)
    let later = () => {
        result = fn.apply(context, args)
        return result
    }
    return function () {
        args = arguments
        context = this
        if (!time) {
            time = window.setTimeout(later, option.delay)
        }
    }
}

執行以上的函數會發現,只能執行一次。因此,我們需要給一個時間差,根據時間差與delay的比較,來判斷是否需要重啟定時器。

function debounce (fn, option) {
  var [time, start, currStart, context, args, result] = []
  let setting = {
    delay: 300
  }
  option = Object.assign({}, setting, option)
  let later = () => {
    let currStart = +new Date() - start
    if (option.delay > currStart) {
      time = window.setTimeout(later, option.delay)
    } else {
      time = null
      result = fn.apply(context, args)
    }
  }
  return function () {
      args = arguments
      context = this
      start = +new Date() // 反覆調用事件的時間
      if (!time) {
        time = window.setTimeout(later, option.delay)
      }
  }
}

如果希望第一次調用事件時就執行函數,而不是等待delay的時間。可以傳入immediate參數。

// fn需要防抖的函數, option可選
function debounce (fn, option) {
  let [time, result, start, context, args] = []
  let setting = {
    delay: 300,
    immediate: false    // 第一次調用事件時是否要立即執行,預設為不立即執行
  }
  // setting為預設參數, 如果傳入option, 則覆蓋setting參數。
  option = Object.assign({}, setting, option)
  let later = () => {
    let currStart = +new Date() - start
    if (option.delay > currStart && currStart >= 0) {
      time = window.setTimeout(later, option.delay)
    } else {
      time = null
      result = fn.apply(context, args)
    }
  }
  return function () {
    args = arguments
    context = this
    start = +new Date()
    if (!time) {
      time = window.setTimeout(later, option.delay)
    }
    // 調用事件時立即執行
    if (option.immediate) {
      result = fn.apply(context, args)
    }
    return result
  }
}

在用在resize事件時

function event (e) {
  console.log(e)
}
let a = debounce(event, {
  delay: 500,
  immediate: fasle
})
window.addEventListener('resize', e => {
  a(e)
})
函數節流

函數節流的實現原理與防抖大同小異。也是通過閉包和定時器來實現。但節流會在一定時間內重覆調用時,將原來的定時器清除掉。假如在500秒內重覆調用了resize時間,那麼只有在最後一次等待delay時間才會執行。

function throttle (fn, option) {
  let time = null
  let start = null
  let setting = {
    delay: 300,
    mustRunTime: 500,   // 在500內必須執行。如在resize事件時,按住不放超過500ms之後就必須執行函數。
    immediate: false
  }
  option = Object.assign({}, setting, option)
  return function () {
    let args = arguments
    let context = this
    let currStart = +new Date()
    if (!start) {
      start = currStart
    }
    // 初始調用resise函數時立即執行函數,而不用等待delay的時間
    if (option.immediate || currStart - start > option.mustRunTime) {
      fn.apply(context, args)
      option.immediate = false
      start = currStart
    } else {
      window.clearTimeout(time)
      time = window.setTimeout(() => {
        fn.apply(context, args)
      }, option.delay)
    }
  }
}

節流的調用方式與防抖相同

function event (e) {
  console.log(e)
}
let a = throttle(event, {
  delay: 500,
  mustRunTime: false,
  immediate: fasle
})
window.addEventListener('resize', e => {
  a(e)
})

最後

最後想說的是,函數節流與防抖的目的都是相同的,在執行函數的請求停止了一段時間之後才執行。
但是我在節流上做了處理,如果超過一定時間就會立即執行函數。

如果需要避免某些事件的調用引出瀏覽器的多次計算,我更加傾向於使用函數節流的方式來實現。本質上,兩種方式都可以噠。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • fetch方法要想於後臺專遞cookie必須添加屬性 註意:這個屬性不是設置在headers里的 後臺也必須設置一個header來配合 ...
  • 1 在使用easyui之前必須要導入壓縮好的完整的easy-ui的js包。 2 其次要在html頁面中引入 具體操作如下: Basic Panel - jQuery EasyUI Demo 3 準備數據,把要表示成為table的對象在資料庫中建立一個表。最好是資料庫中的列的名稱與對象屬性同名。 這樣... ...
  • 前言:這是筆者學習之後自己的理解與整理。如果有錯誤或者疑問的地方,請大家指正,我會持續更新! DOM變動事件 變動事件(MutationEvent)能在DOM中的某一部分發生變化時給出提示,這類事件非常有用,但都只能使用 DOM2 級事件處理程式,且由於瀏覽器相容性不好,所以用的不廣泛; 刪除節點變 ...
  • gdom框架是我開發的一款dom和字元串處理框架,目前版本是1.0.0. 使用方法跟jquery是差不多的, 會用jquery就會用gdom,目前 1.0.0版本的選擇器完全支持CSS3選擇器.沒有做IE的低版本相容。 gdom下載地址:https://github.com/ghostwu/gdom ...
  • 1.打開工具--插件開發--新建代碼片段 會出現下圖: 2.在<![CDATA[和]]>內寫下你要的代碼片段,註意的是代碼片段要靠最左邊。 3.設置快捷鍵,把下麵tabTrigger標簽的註釋打開,中間的h就是你的快捷鍵。 4.Ctrl+s保存。名字隨便起,但是尾碼名必須是.sublime-snip ...
  • 序 嚴格的來說,這是我第一個完全投入的開源項目,它的出現是為了統一移動H5中的下拉刷新,想通過一套框架,多主題拓展方式,適應於任意需求下的任意下拉刷新場景。 另外,這個項目作為獨立項目存在,希望能有更多的人參與進來! " " "【minirefresh】優雅的H5下拉刷新。零依賴,高性能,多主題,易 ...
  • 常用命令 git速度快,分散式, 回到過去,未來,版本 使用git會在當前目錄下,產生一個.git文件,記錄 多端共用 團隊協作 衝突需要手動解決 svn和git對比 svn叫集中式,集中存放,有一個中央伺服器,如果中央伺服器報廢,所有的文件將癱瘓 每個文件夾裡面都有.svn文件,速度慢 git叫分 ...
  • vue.js環境配置以及實例運行簡明教程 聲明:本文檔編寫參考如下兩篇博客,是對它們的修改與補充,歡迎點擊鏈接查看原文: 原文1:vue.js在windows本地下搭建環境和創建項目 原文2:Vue.js開發環境搭建 原文1:vue.js在windows本地下搭建環境和創建項目 原文2:Vue.js ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...