节流(throttle)与防抖(debounce)

运行案例

运行代码

<div id="content"
  style="height:150px;line-height:150px;text-align:center; color: #fff;background-color:#ccc;font-size:80px;">
</div>
<script>
  let num = 1;
  const content = document.getElementById('content');
  function count() {
    content.innerHTML = num++;
  };
  content.onmousemove = count;
</script>

运行效果

mousemove

当鼠标在 div(灰色)区域中移动的时候会持续地去触发该事件导致频繁执行函数。

防抖(debounce)

所谓防抖,就是指触发事件后 n 秒后才执行函数,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。

防抖函数分为非立即执行版和立即执行版。

防抖(debounce)非立即执行版:

非立即执行版触发事件后函数不会立即执行,而是在 n 秒后执行,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。

/**
* @description: 
* @param {Function}  func 事件函数
* @param {Number}    wait 事件设置的延迟事件 (1000 = 1 秒)
* @return (Function): 返回新的 debounced(防抖动)函数。
*
*/
function debounce(func, wait) {
  let timeout;
  return function () {
    /**
    *  const context = this;
    *  const args = [...arguments];
    *  这两行代码来获取 this 和 参数,是为了让 防抖(debounce) 函数最终返回的函数 this 指向不变以及依旧能接受到携带的参数。
    *
    */  
    const context = this;
    const args = [...arguments];
    if (timeout) clearTimeout(timeout);
    timeout = setTimeout(() => {
      func.apply(context, args)
    }, wait);
  }
}
/**
*  content.onmousemove = count;
*  修改mousemove事件绑定的函数
*  content.onmousemove = debounce(count,1000);
*
*/
content.onmousemove = debounce(count,1000);

运行效果

nomousermove

当鼠标在 div(灰色)区域中移动的时候不会触发执行函数,需要在停留在触发事件后函数 1 秒后才执行,而如果触发事件后的 1 秒内移动鼠标,则会重新计算函数执行时间。

防抖(debounce)立即执行版:

/**
* @description: 
* @param {Function}  func 事件函数
* @param {Number}    wait 事件设置的延迟事件 (1000 = 1 秒)
* @return (Function): 返回新的 debounced(防抖动)函数。
*
*/
function debounce(func,wait) {
  let timeout;
  return function () {
      const context = this;
      const args = [...arguments];
      if (timeout) clearTimeout(timeout);
      const callNow = !timeout;
      timeout = setTimeout(() => {
          timeout = null;
      }, wait)
      if (callNow) func.apply(context, args)
  }
}
/**
*  content.onmousemove = count;
*  修改mousemove事件绑定的函数
*  content.onmousemove = debounce(count,1000);
*
*/
content.onmousemove = debounce(count,1000);

运行结果

首次会运行一次。当鼠标在 div(灰色)区域中移动的时候不会触发执行函数,需要在停留在触发事件后函数 1 秒后才执行,而如果触发事件后的 1 秒内移动鼠标,则会重新计算函数执行时间。

防抖函数整合版

/**
* @desc 函数防抖
* @param func (function) 函数
* @param wait (number) 延迟执行毫秒数
* @param immediate (boolean) true 表立即执行,false 表非立即执行
* @return (Function): 返回新的 debounced(防抖动)函数。
*
*/
function debounce(func, wait, immediate) {
    let timeout;
    let result;
    return function () {
        const context = this;
        const args = arguments;
        if (timeout) clearTimeout(timeout);
        if (immediate) {
            const callNow = !timeout;
            timeout = setTimeout(function () {
                timeout = null;
            }, wait)
            if (callNow) result = func.apply(context, args)
        }
        else {
            timeout = setTimeout(function () {
                func.apply(context, args)
            }, wait);
        }
        return result;
    }
}

Lodash库防抖(debounce)函数

/**
*  @desc 创建一个 debounced(防抖动)函数,该函数会从上一次被调用后,延迟 `wait` 毫秒后调用 `func` 方法。 
*  @param func (Function): 要防抖动的函数。
*  @param [wait=0] (number): 需要延迟的毫秒数。
*  @param [options={}] (Object): 选项对象。
*  @param [options.leading=false] (boolean): 指定在延迟开始前调用。
*  @param [options.maxWait] (number): 设置 func 允许被延迟的最大值。
*  @param [options.trailing=true] (boolean): 指定在延迟结束后调用。
*  @return (Function): 返回新的 debounced(防抖动)函数。
*
*/

_.debounce(func, [wait=0], [options={}])

_.debounce提供一个 options(选项) 对象决定如何调用 func 方法,主要是以下三个:

  • leading,函数在每个等待时延的开始被调用,默认值为false
  • trailing,函数在每个等待时延的结束被调用,默认值是true
  • maxwait,最大的等待时间,因为如果debounce的函数调用时间不满足条件,可能永远都无法触发,因此增加了这个配置,保证大于一段时间后一定能执行一次函数。

leadingtrailing的组合,可以实现不同的调用效果:

  • leading-falsetrailing-true:默认情况,即在延时结束后才会调用函数。
  • leading-truetrailing-true:在延时开始时就调用,延时结束后也会调用。
  • leading-true , trailing-false:只在延时开始时调用。

节流(throttle)

连续触发事件但只会在规定时间内只执行一次函数。 节流会稀释函数的执行频率。

节流(throttle)时间戳版:

/**
* @desc 函数节流
* @param func (function) 函数
* @param wait (number) 延迟执行毫秒数
* @return (Function): 返回新的 throttle(防抖动)函数。
*
*/
function throttle(func, wait) {
    var previous = 0;
    return function() {
        let now = Date.now();
        let context = this;
        let args = arguments;
        if (now - previous > wait) {
            func.apply(context, args);
            previous = now;
        }
    }
}
/**
*  content.onmousemove = count;
*  修改mousemove事件绑定的函数
*  content.onmousemove = throttle(count,1000);
*
*/
content.onmousemove = throttle(count,1000);

运行结果

throttle

当鼠标在 div(灰色)区域中移动的时候会持续触发事件,函数会立即执行,并且每 1s 执行一次。

节流(throttle)定时器版:

/**
* @desc 函数节流
* @param func (function) 函数
* @param wait (number) 延迟执行毫秒数
* @return (Function): 返回新的 throttle(防抖动)函数。
*
*/
function throttle(func, wait) {
    let timeout;
    return function() {
        let context = this;
        let args = arguments;
        if (!timeout) {
            timeout = setTimeout(() => {
                timeout = null;
                func.apply(context, args)
            }, wait)
        }

    }
}
/**
*  content.onmousemove = count;
*  修改mousemove事件绑定的函数
*  content.onmousemove = throttle(count,1000);
*
*/
content.onmousemove = throttle(count,1000);

运行结果

setThrottle

当鼠标在 div(灰色)区域中移动的时候会持续触发事件,函数会立即执行,并且每 1s 执行一次。在停止触发事件后,函数还会再执行一次。

节流函数整合版:

/**
*  @desc 函数节流
*  @param func 函数
*  @param wait 延迟执行毫秒数
*  @param type 1 表时间戳版,2 表定时器版
*  @return (Function): 返回新的 throttle(防抖动)函数。
*
*/
function throttle(func, wait ,type) {
    if(type===1){
        let previous = 0;
    }else if(type===2){
        let timeout;
    }
    return function() {
        let context = this;
        let args = arguments;
        if(type===1){
            let now = Date.now();
            if (now - previous > wait) {
                func.apply(context, args);
                previous = now;
            }
        }else if(type===2){
            if (!timeout) {
                timeout = setTimeout(() => {
                    timeout = null;
                    func.apply(context, args)
                }, wait)
            }
        }

    }
}

Lodash库节流(throttle)函数

/**
*  func (Function): 要节流的函数。
*  @param [wait=0] (number): 需要节流的毫秒。
*  @param [options={}] (Object): 选项对象。
*  @param [options.leading=true] (boolean): 指定调用在节流开始前。
*  @param [options.trailing=true] (boolean): 指定调用在节流结束后。
*  @return (Function): 返回节流的函数	
/
_.throttle(func, [wait=0], [options={}])

防抖(throttle)就是设置了maxwait的节流(debounce)。防抖(throttle)同样提供了leadingtrailing参数,与节流(debounce)含义相同。

_.throttle提供一个 options(选项) 对象决定如何调用 func 方法,主要是以下三个:

  • leading,函数在每个等待时延的开始被调用,默认值为false
  • trailing,函数在每个等待时延的结束被调用,默认值是true

leadingtrailing的组合,可以实现不同的调用效果:

  • leading-falsetrailing-true:默认情况,即在延时结束后才会调用函数。
  • leading-truetrailing-true:在延时开始时就调用,延时结束后也会调用。
  • leading-true , trailing-false:只在延时开始时调用。

参考文章

https://github.com/mqyqingfeng/Blog/issues/22

https://github.com/mqyqingfeng/Blog/issues/26

https://blog.csdn.net/duola8789/article/details/78871789

posted @ 2020-08-26 11:32  Scok  阅读(830)  评论(0编辑  收藏  举报