欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript函數(shù)中的防抖與節(jié)流原生實現(xiàn)及第三方庫的使用

 更新時間:2023年10月20日 09:08:13   作者:itclanCoder  
當你頻繁的觸發(fā)用戶界面時,會不停的觸發(fā)事件處理函數(shù),可能導致界面卡頓,瀏覽器奔潰,頁面空白等情況,而解決這一問題的,正是函數(shù)節(jié)流與函數(shù)防抖,所以本文將給大家介紹一下JavaScript函數(shù)中的防抖與節(jié)流原生實現(xiàn)及第三方庫的使用,需要的朋友可以參考下

前言

在開發(fā)中,我們經(jīng)常會遇到需要頻繁觸發(fā)某個函數(shù)的情況,比如:

  • 監(jiān)聽滾動條的變化,當滾動條的位置發(fā)生變化時,需要執(zhí)行某個函數(shù)
  • 監(jiān)聽鼠標的移動,當鼠標的位置發(fā)生變化時,需要執(zhí)行某個函數(shù)
  • 監(jiān)聽鍵盤的按鍵,當鍵盤的某個按鍵被按下時,需要執(zhí)行某個函數(shù)

當用戶頻繁的與UI界面操作交互時,例如:窗口調(diào)整(觸發(fā)resize),頁面滾動,上拉加載(觸發(fā)scroll),表單的按鈕提交,商城搶購瘋狂的點擊(觸發(fā)mousedown),而實時的搜索(keyup,input),拖拽等

當你頻繁的觸發(fā)用戶界面時,會不停的觸發(fā)事件處理函數(shù),換而言之,當出現(xiàn)連續(xù)點擊,上拉加載,實時搜索,對DOM元素頻繁操作,請求資源加載等耗性能的操作,可能導致界面卡頓,瀏覽器奔潰,頁面空白等情況

而解決這一問題的,正是函數(shù)節(jié)流與函數(shù)防抖

函數(shù)節(jié)流

定義: 節(jié)約(減少)觸發(fā)事件處理函數(shù)的頻率,連續(xù)每隔一定的時間觸發(fā)執(zhí)行的函數(shù),它是優(yōu)化高頻率執(zhí)行一段js代碼的一種手段 特點: 不管事件觸發(fā)有多頻繁,都會保證在規(guī)定的間隔時間內(nèi)真正的執(zhí)行一次事件處理函數(shù),只會讓一個函數(shù)在某個時間窗口內(nèi)執(zhí)行一次,若在時間窗口內(nèi)再次觸發(fā),則重新計算時間

應用場景: 常用于鼠標連續(xù)多次點擊click事件,鼠標移動mousemove,拖拽,窗口尺寸改動(resize),鼠標滾輪頁面上拉(onScroll),上拉刷新懶加載

原理: 通過判斷是否達到一定的時間來觸發(fā)函數(shù),若沒有規(guī)定時間則使用計時器進行延遲,而下一次事件則會重新設定計時器,它是間隔時間執(zhí)行

通常與用戶界面高頻的操作有:

  • 鼠標滾輪頁面上拉(onScroll),下拉刷新懶加載
  • 窗口尺寸改動(onresize)
  • 拖拽

若是高頻操作,若不進行一定的處理,必然會造成多次數(shù)據(jù)的請求,服務器的壓力,這樣代碼的性能是非常低效的,影響性能,降低這種頻繁操作的一個重要的手段,就是降低頻率,通過節(jié)流控制,也就是讓核心功能代碼在一定的時間,隔多長時間內(nèi)執(zhí)行一次

節(jié)流就是保證一段時間內(nèi)只執(zhí)行一次核心代碼

你可以聯(lián)想生活中節(jié)約用水(三峽大壩設置很多水閘)的例子:

高頻事件就像是一個大開的水龍頭,水流源源不斷的大量流出,就像代碼在不斷的執(zhí)行,若不加以控制,就會造成資源的一種浪費 對應頁面中的,若是表單中連續(xù)點擊提交按鈕,監(jiān)聽滾動事件,連續(xù)下拉加載等請求服務器的資源

要節(jié)流,擰緊水龍頭,要它的流水頻率降低,每隔一段時間滴一滴水的,從而節(jié)省資源

在代碼中的體現(xiàn)就是:設置一定時器,讓核心功能代碼,隔間段的去執(zhí)行

下面是一個鼠標滾輪,節(jié)流操作實現(xiàn):類似連續(xù)操作的,都是如此,連續(xù)點擊按鈕,上拉加載

節(jié)流方式一:時間戳+定時器

/* throttle1函數(shù),節(jié)流實現(xiàn)方式1:時間戳+定時器
    	 *  @params method,duration 第一個參數(shù)為事件觸發(fā)時的真正要執(zhí)行的函數(shù)
    	 *  第二個參數(shù)duration表示為定義的間隔時間
    	 *
    	 *  原理:通過判斷是否達到一定的時間來觸發(fā)函數(shù),若沒有規(guī)定時間則使用計時器進行延遲,而下一次事件則會重新設定計時器,它是間隔時間執(zhí)行,不管事件觸發(fā)有多頻繁,都會保證在規(guī)定內(nèi)的事件一定會執(zhí)行一次真正事件處理函數(shù)
    	 *
    	 * */
function throttle1(method, duration) {
    var timer = null;
    var prevTime = new Date();   // 之前的時間
return function() {
    var that = this,
        currentTime = new Date(),          // 獲取系統(tǒng)當前時間
        resTime = currentTime - prevTime;  // 時間戳
    // 打印本次當前的世間和上次世間間隔的時間差
    console.log("時間差", resTime);
    // 當前距離上次執(zhí)行時間小于設置的時間間隔
    if(resTime < duration) {
        // 清除上次的定時器,取消上次調(diào)用的隊列任務,重新設置定時器。這樣就可以保證500毫秒秒內(nèi)函數(shù)只會被觸發(fā)一次,達到了函數(shù)節(jié)流的目的
        clearTimeout(timer);
        timer = setTimeout(function(){
            prevTime = currentTime;
            method.apply(that);
        }, duration)
    }else { // 當前距離上次執(zhí)行的時間大于等于設置的時間時,直接執(zhí)行函數(shù)
        // 記錄執(zhí)行方法的時間
        prevTime = currentTime;
        method.apply(that);
    }
    
}
}

// 事件觸發(fā)的方法(函數(shù)),函數(shù)節(jié)流1
function handleJieLiu1(){
console.log("節(jié)流方式1");
}   

var handleJieLiu1 = throttle1(handleJieLiu1, 500);
document.addEventListener('mousewheel', handleJieLiu1);

節(jié)流方式二:重置一個開關變量+定時器:

/*
* throttle2函數(shù)節(jié)流實現(xiàn)方式2:重置一個開關變量+定時器
* @params method,duration形參數(shù)與上面的含義一致
* @return 返回的是一個事件處理函數(shù)
*
* 在throttle2執(zhí)行時定義了runFlag的初始值,通過閉包返回一個匿名函數(shù)作為事件處理函數(shù),
*
* 在返回的函數(shù)內(nèi)部判斷runFlag的狀態(tài)并確定執(zhí)行真正的函數(shù)method還是跳出,

每次執(zhí)行method后會更改runFlag的狀態(tài),通過定時器在durtion該規(guī)定的間隔時間內(nèi)重置runFlag鎖的狀態(tài)
* 
*/
function throttle2(method, duration){
  // 當前時間間隔內(nèi)是否有方法執(zhí)行,設置一個開關標識
  var runFlag = false;
// 返回一個事件處理函數(shù)
return function(e) {
    // 判斷當前是否有方法執(zhí)行,有則什么都不做,若為true,則跳出
    if(runFlag){
        return false;
    }
    // 開始執(zhí)行
    runFlag = true;
    // 添加定時器,在到達時間間隔時重置鎖的狀態(tài)
    setTimeout(function(){
        method(e);
        // 執(zhí)行完畢后,聲明當前沒有正在執(zhí)行的方法,方便下一個時間調(diào)用
        runFlag = false;
    }, duration)
}
} 
// 事件觸發(fā)的方法(函數(shù)),函數(shù)節(jié)流2
function handleJieLiu2(){
console.log("節(jié)流方式2");
}
var handleJieLiu2 = throttle2(handleJieLiu2, 500);
document.addEventListener('mousewheel', handleJieLiu2);

上面兩種實現(xiàn)函數(shù)節(jié)流的方式都可以達到防止用戶頻繁操作而引起重復請求資源的

當鼠標滾輪不斷滾動時,事件處理函數(shù)的執(zhí)行順序不一樣

當給一個大范圍的時間內(nèi),比如:1小時內(nèi),每幾分鐘執(zhí)行一次,超過一小時不在執(zhí)行,推薦使用第一種函數(shù)節(jié)流的方式

如果僅僅要求間隔一定時間執(zhí)行一次,推薦使用第二種函數(shù)節(jié)流的方式

函數(shù)防抖

定義:防止抖動,重復的觸發(fā),頻繁操作,核心在于,延遲事件處理函數(shù)的執(zhí)行,一定時間間隔內(nèi)只執(zhí)行最后一次操作,就是當函數(shù)被觸發(fā)后,只有在上一次函數(shù)執(zhí)行完,一段時間后,才會再次觸發(fā)函數(shù)。 例如:表單多次提交,推薦使用防抖

換句話說,也就是當連續(xù)觸發(fā)事件時并沒有執(zhí)行事件處理函數(shù),只有在某一階段連續(xù)觸發(fā)的最后一次才執(zhí)行,它遵循兩個條件

  • 必須要等待一段時間

  • 上一次觸發(fā)的時間間隔要大于設定值才執(zhí)行

特點: 某段時間內(nèi)只執(zhí)行一次

在生活中,你可以想象公交司機等人上車后,才出站一樣

應用場景: 常應用于輸入框事件keydown,keyup,搜索聯(lián)想查詢,只有在用戶停止鍵盤輸入時,才發(fā)送Ajax請求

原理: 它是維護一個計時器,規(guī)定在duration(延遲)時間后出過事事件處理函數(shù),但是在duration時間內(nèi)再次觸發(fā)的話,都會清除當前的timer重新計時,這樣一來,只有最后一次操作事件處理函數(shù)才被真正的觸發(fā)

具體代碼如下所示:

 /*
*  函數(shù)防抖
*  例如:假定時間間隔時500ms,頻繁不同的操作5s,且每兩次執(zhí)行時間小于等于間隔500ms
*  那么最后只執(zhí)行了1次,也就是每一次執(zhí)行時都結(jié)束上一次的執(zhí)行
*  @params method,duration,與上面一致
*
*  原理:它是維護一個計時器,規(guī)定在duration時間后出發(fā)時間處理函數(shù),但是在duration時間內(nèi)再次出發(fā)的化,都會清除當前的timer重新計時,這樣一來,只有最后一次操作事件處理函數(shù)才被真正的觸發(fā)
*
* 一般用于輸入框事件,常用場景就是表單的搜索或者聯(lián)想查詢,如果不使用防抖會連續(xù)發(fā)送請求,增加服務器的壓力,使用防抖后,會在用戶輸入要查詢的關鍵詞后才發(fā)送請求,百度搜索就是這么實現(xiàn)的
*
* 
*/
function debounce(method, duration) {
var timer = null;
return function(){
    var that = this,
        args = arguments;
    // 在本次調(diào)用之間的一個間隔時間內(nèi)若有方法在執(zhí)行,則終止該方法的執(zhí)行
    if(timer) {
        clearTimeout(timer);
    }
    // 開始執(zhí)行本次調(diào)用
    timer = setTimeout(function(){
        method.apply(that,args);
    }, duration)

}

}
    // 事件觸發(fā)的方法(函數(shù)),防抖
function handleFangDou(){
    console.log("函數(shù)的防抖",new Date());
}
var handleFangDou = debounce(handleFangDou, 500);
var oInput = document.querySelector("#input"); // 獲取input元素
oInput.addEventListener('keyup',handleFangDou);

如上輸入框效果所示,每當輸入框輸入后,鍵盤彈起時,執(zhí)行事件處理函數(shù),而不應該是鍵入內(nèi)容時都觸發(fā)一次事件處理函數(shù)

同理,搜索引擎,表單聯(lián)想查詢功能時,不是根據(jù)用戶鍵入的字母,數(shù)字,內(nèi)容同時進行Ajax數(shù)據(jù)請求的,如果每鍵入一個字母都觸發(fā)一次數(shù)據(jù)請求,那就耗性能了的 應當是用戶停止輸入的時候才去觸發(fā)查詢請求,這個時候就用到函數(shù)防抖了的

表單的多次提交,百度搜索等都是用的防抖實現(xiàn)的

小結(jié):

共同點: 都是解決頻繁操作觸發(fā)事件處理函數(shù),引起頁面卡頓,不流暢等性能問題,都是通過設置延時計時器邏輯來提升性能,以減少http請求次數(shù),節(jié)約請求資源

不同點:函數(shù)節(jié)流,間隔時間內(nèi)執(zhí)行事件處理函數(shù),而函數(shù)防抖,一定時間間隔內(nèi)只執(zhí)行最后一次操作

直接引入lodash庫

如果自己不原生手動實現(xiàn),可以直接安裝yarn add lodash,然后引入

// 函數(shù)接口
npm i -S lodash.throttle;

import throttle from 'lodash.throttle'; // 引入lodash.throttle庫

// 事件觸發(fā)的方法(函數(shù)),節(jié)流
function handleThrottle(){
    console.log("函數(shù)的節(jié)流",new Date());
}

throttle(handleThrottle, 500); // 將觸發(fā)事件處理函數(shù)作為第一個參數(shù)傳入,第二個參數(shù)為間隔的時間,這里是500毫秒

下面是函數(shù)防抖的實現(xiàn)

在終端下通過npm或者cnpmyarn的方式安裝第三方庫

npm i -S loadsh.debounce
或者
cnpm install -S loadsh.debounce

在組件中使用

import debounce from 'lodash.debounce'; // 函數(shù)防抖

function handleDebounce() {
    console.log("函數(shù)的防抖", new Date());
}                        

debounce(handleDebounce, 500);

自己原生實現(xiàn)函數(shù)防抖

// 自己封裝一個debounce函數(shù)用于防抖
  debounce(method, duration) {
          var timer = null;
         /*return function(){
            var that = this,
                args = arguments;
            // 在本次調(diào)用之間的一個間隔時間內(nèi)若有方法在執(zhí)行,則終止該方法的執(zhí)行
            if(timer) {
              clearTimeout(timer);
            }
            // 開始執(zhí)行本次調(diào)用
            timer = setTimeout(function(){
              method.apply(that,args);
            }, duration)

          }*/
          // 上面的return匿名函數(shù)可以用Es6的箭頭函數(shù),以下寫法與上面等價,最簡潔的寫法,但是沒有上面的代碼好理解
          return (...args) => {
            clearTimeout(timer);
            timer = setTimeout(() =>   method(...args), duration)
          }

  }

當然對于上面的代碼,還是可以優(yōu)化一下的,對于回調(diào)函數(shù),在Es6中,常用于箭頭函數(shù)來處理,這樣會省去不少麻煩

例如:this的指向問題

如下所示:debouce函數(shù)最簡易的封裝

你也可以把上面的定時器初始值放在debouce函數(shù)作為第三個形參數(shù)設置,也是可以的

debounce(method, duration, timer = null) {
          return (...args) => {
            clearTimeout(timer);
            timer = setTimeout(() => {
              method(...args)
            }, duration)
          }

  }

如果自己封裝throttledebounce函數(shù),可以單獨封裝到一個文件對外暴露就可以了,在需要用它們的地方,通過import引入即可,在代碼中直接調(diào)用就可以

在根目錄下(以你自己的為準)創(chuàng)建一個throttle.js 通過export default 暴露出去

/*
*  @authors 川川 (itclancode@163.com)
 * @ID suibichuanji
 * @date 2023-10-19
   @desc 封裝節(jié)流函數(shù)
*  @param method,duration:method事件處理函數(shù),duration:間隔的時間
*  @return 匿名函數(shù)
*  原理: 通過判斷是否達到一定的時間來觸發(fā)函數(shù),
*  若沒有規(guī)定時間則使用計時器進行延遲,而下一次事件則會重新設定計時器
*  它是間隔時間執(zhí)行,不管事件觸發(fā)有多頻繁
*  都會保證在規(guī)定內(nèi)的事件一定會執(zhí)行一次真正事件處理函數(shù)
* 
 */
function throttle(method, duration) {
    var timer = null;
    var prevTime = new Date(); // 之前的時間
    return function() {
      var that = this,
        currentTime = new Date(), // 獲取系統(tǒng)當前時間
        resTime = currentTime - prevTime; // 時間戳
      // 打印本次當前的世間和上次世間間隔的時間差
      console.log("時間差", resTime);
      // 當前距離上次執(zhí)行時間小于設置的時間間隔
      if (resTime < duration) {
        // 清除上次的定時器,取消上次調(diào)用的隊列任務,重新設置定時器。這樣就可以保證500毫秒秒內(nèi)函數(shù)只會被觸發(fā)一次,達到了函數(shù)節(jié)流的目的
        clearTimeout(timer);
        timer = setTimeout(function() {
          prevTime = currentTime;
          method.apply(that);
        }, duration)
      } else { // 當前距離上次執(zhí)行的時間大于等于設置的時間時,直接執(zhí)行函數(shù)
        // 記錄執(zhí)行方法的時間
        prevTime = currentTime;
        method.apply(that);
      }

    }
  }
  export default throttle;

然后在需要使用函數(shù)節(jié)流文件中引入

import throttle from './throttle';


throttle(事件觸發(fā)處理函數(shù), 1000);

同理,若是自己封裝debounce函數(shù)的防抖,把它單獨的抽離出去封裝成一個函數(shù),通過export 對外暴露,供其他地方調(diào)用

/**
 * 
 * @authors 川川 (itclancode@163.com)
 * @ID suibichuanji
 * @date  2023-10-19 
 * @version $Id$
 * @description  函數(shù)防抖
 * @param { method, duration} [method是事件處理函數(shù),duration是延遲時間]
 * 原理
 * 原理:它是維護一個計時器,規(guī)定在duration時間后出發(fā)時間處理函數(shù)
 * 但是在duration時間內(nèi)再次出發(fā)的化,都會清除當前的timer重新計時
 * 這樣一來,只有最后一次操作事件處理函數(shù)才被真正的觸發(fā)
 *
 * 一般用于輸入框事件,常用場景就是表單的搜索或者聯(lián)想查詢,
 * 如果不使用防抖會連續(xù)發(fā)送請求,增加服務器的壓力
 * 使用防抖后,會在用戶輸入要查詢的關鍵詞后才發(fā)送請求,百度搜索就是這么實現(xiàn)的
 */
function  debounce(method, duration) {
          var timer = null;
         return function(){
            var that = this,
                args = arguments;
            // 在本次調(diào)用之間的一個間隔時間內(nèi)若有方法在執(zhí)行,則終止該方法的執(zhí)行
            if(timer) {
              clearTimeout(timer);
            }
            // 開始執(zhí)行本次調(diào)用
            timer = setTimeout(function(){
              method.apply(that,args);
            }, duration)

          }

  }

  export default debounce;

如何阻止函數(shù)調(diào)用太快(函數(shù)節(jié)流,兩種方式)或者太多次(函數(shù)防抖),分別用原生JS以第三方庫實現(xiàn)

對于函數(shù)的節(jié)流與防抖是前端提升性能的手段,雖然就幾行代碼,但是面試時,常問不衰,讓你手寫,很多時候,拍拍胸脯,不借助搜索,你還真不一定能寫得出來

在實際的開發(fā)中,函數(shù)的節(jié)流與函數(shù)防抖也是比較頻繁的,可見它的重要性不言而喻

以上就是JavaScript函數(shù)中的防抖與節(jié)流原生實現(xiàn)及第三方庫的使用的詳細內(nèi)容,更多關于JavaScript函數(shù)的防抖與節(jié)流的資料請關注腳本之家其它相關文章!

相關文章

最新評論