防抖和节流原理分析

窗口的resize、scroll、输入框内容校验等操作时,如果这些操作处理函数是较为复杂或页面频繁重渲染等操作时,在这种情况下如果事件触发的频率无限制,会加重浏览器的负担,导致用户体验非常糟糕。此时我们可以采用debounce(防抖)和throttle(节流)的方式来减少触发的频率,同时又不影响实际效果。

防抖

debounce(防抖),简单来说就是防止抖动。

当持续触发事件时,debounce 会合并事件且不会去触发事件当一定时间内没有触发再这个事件时,才真正去触发事件

非立即执行版

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

const debounce = (func, wait, ...args) => {
  let timeout;
  return function(){
    const context = this;
    if (timeout) clearTimeout(timeout);
    timeout = setTimeout(() => {
      func.apply(context, args)
    },wait);
  }
}

如此调用:

content.onmousemove = debounce(count,1000);

立即执行版

立即执行版的意思是触发事件后函数会立即执行,然后 n 秒内不触发事件才能继续执行函数的效果。

const debounce = (func, wait, ...args) => {
  let timeout;
  return function(){
    const context = this;
    if (timeout) cleatTimeout(timeout);
    let callNow = !timeout;
    timeout = setTimeout(() => {
      timeout = null;
    },wait)

    if(callNow) func.apply(context,args)
   }
}

结合版

/**
 * @desc 函数防抖
 * @param func 函数
 * @param wait 延迟执行毫秒数
 * @param immediate true 表立即执行,false 表非立即执行
 */functiondebounce(func,wait,immediate) {
    var timeout;

    returnfunction () {
        var context = this;
        var args = arguments;

        if (timeout) clearTimeout(timeout);
        if (immediate) {
            var callNow = !timeout;
            timeout = setTimeout(function(){
                timeout = null;
            }, wait)
            if (callNow) func.apply(context, args)
        }
        else {
            timeout = setTimeout(function(){
                func.apply(context, args)
            }, wait);
        }
    }
}

节流

throttle(节流),当持续触发事件时,保证隔间时间触发一次事件

持续触发事件时,throttle 会合并一定时间内的事件,并在该时间结束时真正去触发一次事件。

时间戳版

在持续触发事件的过程中,函数会立即执行,并且每 1s 执行一次。

const throttle = (func, wait, ...args) => {
  let pre = 0;
  returnfunction(){
    const context = this;
    let now = Date.now();
    if (now - pre >= wait){
       func.apply(context, args);
       pre = Date.now();
    }
  }
}

定时器版

在持续触发事件的过程中,函数不会立即执行,并且每 1s 执行一次,在停止触发事件后,函数还会再执行一次。

const throttle = (func, wait, ...args) => {
  let timeout;
  return function(){
    const context = this;
    if(!timeout){
      timeout = setTimeout(() => {
        timeout = null;
        func.apply(context,args);
      },wait)
    }
  }
}

结合版

其实时间戳版和定时器版的节流函数的区别就是,时间戳版的函数触发是在时间段内开始的时候,而定时器版的函数触发是在时间段内结束的时候。

/**
 * @desc 函数节流
 * @param func 函数
 * @param wait 延迟执行毫秒数
 * @param type 1 表时间戳版,2 表定时器版
 */functionthrottle(func, wait ,type) {
    if(type===1){
        var previous = 0;
    }elseif(type===2){
        var timeout;
    }

    returnfunction() {
        var context = this;
        var args = arguments;
        if(type===1){
            var now = Date.now();

            if (now - previous > wait) {
                func.apply(context, args);
                previous = now;
            }
        }elseif(type===2){
            if (!timeout) {
                timeout = setTimeout(function() {
                    timeout = null;
                    func.apply(context, args)
                }, wait)
            }
        }

    }
}