Skip to content
On this page

含义

防抖节流都是限制函数的执行次数

  1. 防抖: 通过 setTimeout 的方式,在一定的时间间隔内,将多次触发转为一次触发
  2. 节流: 减少一段时间的触发频率

防抖 debouncer

三个参数(有默认值)

function debouncer(fn, delayTime = 1000, once = false)

  1. fn 执行的函数
  2. delayTime 延时时间
  3. once 前置 / 后置 触发执行的函数
html
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script>
  function btnHandle(a, b, c) {
    console.log("防抖函数", this, ...arguments);
  }

  $("#btn").on("click", debouncer(btnHandle, 1000, true));
  $(window).scroll("click", debouncer(btnHandle, 2000, true));

  //防抖
  function debouncer(fn, delayTime = 1000, once = false) {
    let timer = null;
    return function () {
      // 这个函数this指向没问题

      //如果有计时器就清除
      if (timer) clearTimeout(timer);

      //第一次点击不延时 但是这个直到你不点然后延时结束 在点才会执行 所以once开启就为前置 关闭就为后置
      if (once) {
        //如果是第一次点击直接执行 这里第一次点击时timer为null 下面才会赋值timer
        if (!timer) fn.apply(this, arguments);

        //执行后 如果隔一段时间再点 还是认为 第一次执行 所以延时改一下timer
        timer = setTimeout(() => {
          timer = null;
        }, delayTime);

        return;
      }

      //再开定时器
      timer = setTimeout(() => {
        // fn(...arguments); //箭头函数所以可以arguments 要不然这个arguments取的是settimeout函数的arguments
        // 但是要设置this指向 所以直接用apply
        fn.apply(this, arguments); //因为用了箭头函数 this直接拿了外面的this
      }, delayTime);
    };
  }
</script>

拆分防抖

后置触发

js
function btnHandle(a, b, c) {
  console.log("防抖函数", this, ...arguments);
}

$("#btn").on("click", debouncer(btnHandle, 1000, true));
$(window).scroll("click", debouncer(btnHandle, 2000, true));

//触发多次 只会在停止触发后过段时间执行
function debouncer(fn, delayTime = 1000) {
  let timer = null;
  return function () {
    // 这个函数this指向没问题

    //如果有计时器就清除
    if (timer) clearTimeout(timer);

    //再开定时器
    timer = setTimeout(() => {
      // fn(...arguments); //箭头函数所以可以arguments 要不然这个arguments取的是settimeout函数的arguments
      // 但是要设置this指向 所以直接用apply
      fn.apply(this, arguments); //因为用了箭头函数 this直接拿了外面的this
    }, delayTime);
  };
}

前置触发

js
function btnHandle(a, b, c) {
  console.log("防抖函数", this, ...arguments);
}

$("#btn").on("click", debouncer(btnHandle, 1000, true));
$(window).scroll("click", debouncer(btnHandle, 2000, true));

//触发多次 只会在第一次触发执行 必须停止一段时间 再点才执行
function debouncer(fn, delayTime = 1000) {
  let timer = null;
  return function () {
    // 如果第一次触发直接执行  第一次timer是null
    if (!timer) fn.apply(this.arguments);

    // 清除定时器
    clearInterval(timer);
    // 开定时器
    timer = setTimeout(() => {
      timer = null;
    }, delayTime);
  };
}

ts中的防抖

ts中会不可以随便用this 需要这样写

ts
export default function debouncer(fn: Function, delayTime: number = 1000) {
  let timer: any = null;
  return function (this: any, ...arr: any) {
    // 这个函数this指向没问题

    console.log("this", this);
    console.log("arr", arr);

    //如果有计时器就清除
    if (timer) clearTimeout(timer);

    //再开定时器
    timer = setTimeout(() => {
      // fn(...arguments); //箭头函数所以可以arguments 要不然这个arguments取的是settimeout函数的arguments
      // 但是要设置this指向 所以直接用apply
      fn.apply(this, arr); //因为用了箭头函数 this直接拿了外面的this
    }, delayTime);
  };
}

-----------测试------------

function getOrder() {}
const newGetOrder = debouncer(getOrder, 400)
newGetOrder("search",123,3,312,33,123,321,3,3,32,3)

//打印结果为
this undefined
arr (11)>['search', 123, 3, 312, 33, 123, 321, 3, 3, 32, 3]

节流 throttle

setTimeout 方式

js
function throttle(fn, delayTime = 1000) {
  //节流阀
  let swi = true;
  return function () {
    //如果关闭状态就退出
    if (!swi) return;

    //没关闭的话先关闭
    swi = false;

    //再开定时器
    setTimeout(() => {
      fn.apply(this, arguments);
      //开启节流阀
      swi = true;
    }, delayTime);
  };
}

节流阀可以换为timer

ts
export default function throttle(fn: Function, delayTime: number = 1000) {
  let timer: any = null;

  return function (this: any, ...arr: any) {
    if (timer) return;

    fn.apply(this, arr);

    timer = setTimeout(() => {
      clearTimeout(timer); //这种是不行的 clearTimerout后timer还是有值
      timer = null; //重新赋值timer
      console.log(null);
    }, delayTime);
  };
}

时间戳方式

js
function throttle(fn, delayTime = 1000) {
  let temp = 0;
  return function () {
    let sign = Date.now();

    //当前时间和上次执行时间超过规定时间才执行
    if (sign - temp > delayTime) {
      fn.apply(this, arguments);
      temp = sign;
    }
  };
}