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

Javascript節(jié)流函數(shù)throttle和防抖函數(shù)debounce

 更新時(shí)間:2020年12月03日 09:27:25   作者:王者歸來!  
這篇文章主要介紹了Javascript節(jié)流函數(shù)throttle和防抖函數(shù)debounce,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

問題的引出

  在一些場景往往由于事件頻繁被觸發(fā),因而頻繁地進(jìn)行DOM操作、資源加載,導(dǎo)致UI停頓甚至瀏覽器崩潰。

在這樣的情況下,我們實(shí)際上的需求大多為停止改變大小n毫秒后執(zhí)行后續(xù)處理;而其他事件大多的需求是以一定的頻率執(zhí)行后續(xù)處理。針對這兩種需求就出現(xiàn)了debounce和throttle兩種解決辦法。

1. resize事件

2. mousemove事件

3. touchmove事件

4.scroll事件

throttle 與 debounce

在現(xiàn)在很多的javascript框架中都提供了這兩個函數(shù)。例如 jquery中有throttle和debounce插件, underscore.js ,Lodash.js 等都提供了這兩個函數(shù)。

原理:

首先我們會想到設(shè)置一定的時(shí)間范圍delay,每隔delayms 執(zhí)行不超過一次。

事件處理函數(shù)什么時(shí)候執(zhí)行能? 這里有兩個選擇,一是先執(zhí)行,再間隔delayms來等待;或者是先等待delayms,然后執(zhí)行事件處理函數(shù)。

操作過程中的事件全不管,反正只執(zhí)行一次事件處理。

相同低,這一次的事件處理可以是先執(zhí)行一次,然后后面的事件都不管; 或者前面的都不管,最后操作完了再執(zhí)行一次事件處理。

區(qū)別:

  1. throttle

    如果將水龍頭擰緊直到水是以水滴的形式流出,那你會發(fā)現(xiàn)每隔一段時(shí)間,就會有一滴水流出。

    也就是會說預(yù)先設(shè)定一個執(zhí)行周期,當(dāng)調(diào)用動作的時(shí)刻大于等于執(zhí)行周期則執(zhí)行該動作,然后進(jìn)入下一個新周期。

  2.debounce

    如果用手指一直按住一個彈簧,它將不會彈起直到你松手為止。

    也就是說當(dāng)調(diào)用動作n毫秒后,才會執(zhí)行該動作,若在這n毫秒內(nèi)又調(diào)用此動作則將重新計(jì)算執(zhí)行時(shí)間。

簡單代碼實(shí)現(xiàn)及實(shí)驗(yàn)結(jié)果

那么下面我們自己簡單地實(shí)現(xiàn)下這兩個函數(shù):

throttle 函數(shù):

 window.addEventListener("resize", throttle(callback, 300, {leading:false}));
 window.addEventListener("resize", callback2);
 function callback () { console.count("Throttled"); }
 function callback2 () { console.count("Not Throttled"); }
 /**
 * 頻率控制函數(shù), fn執(zhí)行次數(shù)不超過 1 次/delay
 * @param fn{Function}   傳入的函數(shù)
 * @param delay{Number}  時(shí)間間隔
 * @param options{Object} 如果想忽略開始邊界上的調(diào)用則傳入 {leading:false},
 *             如果想忽略結(jié)束邊界上的調(diào)用則傳入 {trailing:false},
 * @returns {Function}   返回調(diào)用函數(shù)
 */
 function throttle(fn,delay,options) {
   var wait=false;
   if (!options) options = {};
   return function(){
     var that = this,args=arguments;
     if(!wait){
       if (!(options.leading === false)){
         fn.apply(that,args);
       }
       wait=true;
       setTimeout(function () {
         if (!(options.trailing === false)){
           fn.apply(that,args);
         }
         wait=false;
       },delay);
     }
   }
 }

將以上代碼貼入瀏覽器中運(yùn)行,可得到:

下面再看debounce函數(shù)的情況,

debounce 函數(shù):

window.addEventListener("resize", throttle(callback, 300, {leading:false}));
window.addEventListener("resize", callback2);
function callback () { console.count("Throttled"); }
function callback2 () { console.count("Not Throttled"); }
/**
 * 空閑控制函數(shù), fn僅執(zhí)行一次
 * @param fn{Function}   傳入的函數(shù)
 * @param delay{Number}  時(shí)間間隔
 * @param options{Object} 如果想忽略開始邊界上的調(diào)用則傳入 {leading:false},
 *             如果想忽略結(jié)束邊界上的調(diào)用則傳入 {trailing:false},
 * @returns {Function}   返回調(diào)用函數(shù)
 */
function debounce(fn, delay, options) {
  var timeoutId;
  if (!options) options = {};
  var leadingExc = false;

  return function() {
    var that = this,
      args = arguments;
    if (!leadingExc&&!(options.leading === false)) {
      fn.apply(that, args);
    }
    leadingExc=true;
    if (timeoutId) {
      clearTimeout(timeoutId);
    }
    timeoutId = setTimeout(function() {
      if (!(options.trailing === false)) {
        fn.apply(that, args);
      }
      leadingExc=false;
    }, delay);
  }
}

將以上代碼貼入瀏覽器中運(yùn)行,分三次改變窗口大小,可看到,每一次改變窗口的大小都會把開始和結(jié)束邊界的事件處理函數(shù)各執(zhí)行一次:

如果是一次性改變窗口大小,會發(fā)現(xiàn)開始和結(jié)束的邊界各執(zhí)行一次時(shí)間處理函數(shù),請注意與一次性改變窗口大小時(shí) throttle 情況的對比:

underscore.js 的代碼實(shí)現(xiàn)

_.throttle函數(shù)

 /**
 * 頻率控制函數(shù), fn執(zhí)行次數(shù)不超過 1 次/delay
 * @param fn{Function}   傳入的函數(shù)
 * @param delay{Number}  時(shí)間間隔
 * @param options{Object} 如果想忽略開始邊界上的調(diào)用則傳入 {leading:false},
 *             如果想忽略結(jié)束邊界上的調(diào)用則傳入 {trailing:false},
 * @returns {Function}   返回調(diào)用函數(shù)
 */
_.throttle = function(func, wait, options) {
  var context, args, result;
  var timeout = null;
  var previous = 0;
  if (!options) options = {};
  var later = function() {
    previous = options.leading === false ? 0 : _.now();
    timeout = null;
    result = func.apply(context, args);
    if (!timeout) context = args = null;
  };
  return function() {
    var now = _.now();
    if (!previous && options.leading === false) previous = now;
    var remaining = wait - (now - previous);
    context = this;
    args = arguments;
    if (remaining <= 0 || remaining > wait) {
      clearTimeout(timeout);
      timeout = null;
      previous = now;
      result = func.apply(context, args);
      if (!timeout) context = args = null;
    } else if (!timeout && options.trailing !== false) {
      timeout = setTimeout(later, remaining);
    }
    return result;
  };
};

_.debounce函數(shù)

/**
 * 空閑控制函數(shù), fn僅執(zhí)行一次
 * @param fn{Function}   傳入的函數(shù)
 * @param delay{Number}  時(shí)間間隔
 * @param options{Object} 如果想忽略開始邊界上的調(diào)用則傳入 {leading:false},
 *             如果想忽略結(jié)束邊界上的調(diào)用則傳入 {trailing:false},
 * @returns {Function}   返回調(diào)用函數(shù)
 */
_.debounce = function(func, wait, immediate) {
  var timeout, args, context, timestamp, result;

  var later = function() {
    var last = _.now() - timestamp;
    if (last < wait && last > 0) {
      timeout = setTimeout(later, wait - last);
    } else {
      timeout = null;
      if (!immediate) {
        result = func.apply(context, args);
        if (!timeout) context = args = null;
      }
    }
  };

  return function() {
    context = this;
    args = arguments;
    timestamp = _.now();
    var callNow = immediate && !timeout;
    if (!timeout) timeout = setTimeout(later, wait);
    if (callNow) {
      result = func.apply(context, args);
      context = args = null;
    }
    return result;
  };
}; 

參考的文章

Debounce and Throttle: a visual explanation
jQuery throttle / debounce: Sometimes, less is more!
underscore.js

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評論