日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

JS专题之节流函数

發布時間:2023/12/6 javascript 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JS专题之节流函数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
本文共 2000 字,讀完只需 8 分鐘

上一篇文章講了去抖函數,然后這一篇講同樣為了優化性能,降低事件處理頻率的節流函數。

一、什么是節流?

節流函數(throttle)就是讓事件處理函數(handler)在大于等于執行周期時才能執行,周期之內不執行,即事件一直被觸發,那么事件將會按每小段固定時間一次的頻率執行。

打個比方:王者榮耀、英雄聯盟、植物大戰僵尸游戲中,技能的冷卻時間,技能的冷卻過程中,是無法使用技能的,只能等冷卻時間到之后才能執行。

那什么樣的場景能用到節流函數呢?
比如:

  • 頁面滾動和改變大小時需要進行業務處理,比如判斷是否滑到底部,然后進行懶加載數據。
  • 按鈕被高頻率地點擊時,比如游戲和搶購網站。
  • 我們通過一個簡單的示意來理解:

    節流函數可以用時間戳和定時器兩種方式進行處理。

    二、時間戳方式實現

    <div class="container" id="container">正在滑動:0 </div><script> window.onload = function() {var bodyEl = document.getElementsByTagName("body")[0] }var count = 0; window.onmousemove = throttle(eventHandler, 1000);function eventHandler(e) {var containerEl = document.getElementById("container");containerEl.innerHTML = "正在滑動: " + count;count++; }function throttle(func, delay) {var delay = delay || 1000;var previousDate = new Date();var previous = previousDate.getTime(); // 初始化一個時間,也作為高頻率事件判斷事件間隔的變量,通過閉包進行保存。return function(args) {var context = this;var nowDate = new Date();var now = nowDate.getTime();if (now - previous >= delay) { // 如果本次觸發和上次觸發的時間間隔超過設定的時間func.call(context, args); // 就執行事件處理函數 (eventHandler)previous = now; // 然后將本次的觸發時間,作為下次觸發事件的參考時間。}} } </script>

    看時間戳實現版本的效果:

    三、定時器方式實現

    <div class="container" id="container">正在滑動: 0 </div><script> window.onload = function() {var bodyEl = document.getElementsByTagName("body")[0] }var count = 0; window.onmousemove = throttle(eventHandler, 1000);function eventHandler(e) {var containerEl = document.getElementById("container");containerEl.innerHTML = "正在滑動: " + count;count++; }function throttle(func, delay) {var delay = delay || 1000;var timer = null;return function(args) {var context = this;var nowDate = new Date();var now = nowDate.getTime();if (!timer) {timer = setTimeout(function() {func.call(context, args);timer = null;}, delay)}}} </script>

    看看定時器版實現版本的效果:

    三、時間戳和定時器的對比分析

    對比時間戳和定時器兩種方式,效果上的區別主要在于:

    事件戳方式會立即執行,定時器會在事件觸發后延遲執行,而且事件停止觸發后還會再延遲執行一次。

    具體選擇哪種方式取決于使用場景。underscore 把這兩類場景用 leading 和 trailing 進行了表示。

    四、兼容兩種方式, underscore 源碼實現

    underscore 的源碼中就同時實現了時間戳和定時器實現方式,在調用時可以自由選擇要不要在間隔時間開始時(leading)執行,或是間隔時間結束后(trailing)執行。

    具體看偽代碼和示意圖:

    <div class="container" id="container">正在滑動: 0</div><div class="height"></div><script>window.onload = function() {var bodyEl = document.getElementsByTagName("body")[0]}var count = 0;// 事件處理函數function eventHandler(e) {var containerEl = document.getElementById("container");containerEl.innerHTML = "正在滑動: " + count;count++;}var _throttle = function(func, wait, options) {var context, args, result;// 定時器變量默認為 null, 是為了如果想要觸發了一次后再延遲執行一次。var timeout = null;// 上一次觸發事件回調的時間戳。 默認為 0 是為了方便第一次觸發默認立即執行var previous = 0;// 如果沒有傳入 options 參數// 則將 options 參數置為空對象if (!options)options = {};var later = function() {// 如果 options.leading === false// 則每次觸發回調后將 previous 置為 0, 表示下次事件觸發會立即執行事件處理函數// 否則置為當前時間戳previous = options.leading === false ? 0 : +new Date();// 剩余時間跑完,執行事件,并把定時器變量置為空,如果不為空,那么剩余時間內是不會執行事件處理函數的,見 else if 那。timeout = null;result = func.apply(context, args);// 剩余時間結束,并執行完事件后,清理閉包中自由變量的內存垃圾,因為不再需要了。if (!timeout)context = args = null;};// 返回的事件回調函數return function() {// 記錄當前時間戳var now = +new Date();// 第一次執行回調(此時 previous 為 0,之后 previous 值為上一次時間戳)// 并且如果程序設定第一個回調不是立即執行的(options.leading === false)// 則將 previous 值(表示上次執行的時間戳)設為 now 的時間戳(第一次觸發時)// 表示剛執行過,這次就不用執行了if (!previous && options.leading === false)previous = now;// 間隔時間 和 上一次到本次事件觸發回調的持續時間的時間差var remaining = wait - (now - previous);context = this;args = arguments;// 如果間隔時間還沒跑完,則不會執行任何事件處理函數。// 如果超過間隔時間,就可以觸發方法(remaining <= 0)// remaining > wait,表示客戶端系統時間被調整過// 也會立即執行 func 函數if (remaining <= 0 || remaining > wait) {if (timeout) {clearTimeout(timeout);// 解除引用,防止內存泄露timeout = null;}// 重置前一次觸發的時間戳previous = now;// result 為事件處理函數(handler)的返回值// 采用 apply 傳遞類數組對象 argumentsresult = func.apply(context, args);// 引用置為空,防止內存泄露if (!timeout)context = args = null;} else if (!timeout && options.trailing !== false) {// 如果 remaining > 0, 表示在間隔時間內,又觸發了一次事件// 如果 trailing 為真,則會在間隔時間結束時執行一次事件處理函數(handler)// 在從觸發到剩余時間跑完,會利用一個定時器執行事件處理函數,并在定時器結束時把 定時器變量置為空// 如果剩余事件內已經存在一個定時器,則不會進入本 else if 分支, 表示剩余時間已經有一個定時器在運行,該定時器會在剩余時間跑完后執行。// 如果 trailing = false,即不需要在剩余時間跑完執行事件處理函數。// 間隔 remaining milliseconds 后觸發 later 方法timeout = setTimeout(later, remaining);}// 回調返回值return result;};};window.onmousemove = _throttle(eventHandler, 1000);</script>

    下面是我畫的示意圖:

    大致總結一下代碼對事件處理邏輯的影響:

  • 如果 leading 為真,那么綠色意味著間隔時間的開始會立即執行,第一次觸發也會立即執行。
  • 如果 trailing 為真,那么從藍紫色的豎細線后的剩余事件,會跑一個定時器,定時器在時間間隔結束時再執行一次事件處理函數。
  • 如果 leading 不為真,那么第一次事件觸發不會立即執行。
  • 如果 trailing 不為真,最后一次事件觸發后,不然再執行一次事件處理函數。
  • 節流和去抖的常見場景

  • 輸入框打字輸入完后才開始異步請求數據校驗內容:去抖
  • 下拉滾動條判斷是否到底部,進行懶加載數據:去抖和節流都可以,判斷是否到底的方式不同
  • 活動網站、游戲網站,按鈕被瘋狂點擊:節流
  • 六、總結

    去抖和節流函數都是為了降低高頻率事件觸發的事件處理頻率,從而優化網頁中大量重繪重排帶來的性能問題。

    其區別在于去抖會在高頻率事件觸發時,只執行一次,節流會在滿足間隔時間后執行一次。去抖的 immediate,節流中的 leading, trailing 都是為了盡可能滿足這類工具函數的不同使用場景。

    總結

    以上是生活随笔為你收集整理的JS专题之节流函数的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。