JavaScript函数防抖与截流

一、在前端开发当中,有些交互事件,会频繁触发。这样会导致我们的页面渲染性能,如果频繁触发接口调用的话,会直接导致服务器性能的浪费。

     例如:键盘事件 keyup作为测试

  • 未做处理:
    函数被调用:0
const count = document.getElementById('count');
const pt = document.getElementById('ipt');
let init = 0
pt.onkeyup = function() { 
  count.innerText = ++init
}

运行效果

       每次输入都会触发事件的执行。如果我们用这样的方式去检测:当前用户输入的用户名是否可用?如此高频率的触发不仅是极大的浪费,而且用户还没有输入完成就检测,对用户的提示也不好。应该等用户输入完了,我们再去触发函数,下面我们优化一下:

  • 未做处理:
    函数被调用:0
  • 防抖处理:
    函数被调用:0
const Count2 = document.getElementById('count2');
const pt2 = document.getElementById('pt2');
// 设置一个默认值 500ms
const debounce = (fn, wait = 500) => {
  let time = null
  return function(arguments) {
    const _this = this, args = arguments
    clearTimeout(time)
    time = setTimeout(() => {
      fn.apply(_this, [args])
}, wait)
  }}
let init2 = pt2.onkeyup = debounce(function() {
Count2.innerText = ++init2

运行效果:

JavaScript函数防抖与截流_第1张图片

       可以看到,加了防抖函数之后,当我们在频繁输入的时候,函数并没有执行, 只有在函数指定的间隔内(500ms)不再输入了,才会执行函数。如果在时间间隔之内继续输入,会触发函数重新计数。

       函数防抖:在事件触发后的n秒之后,再去执行真正需要执行的函数,如果在这n秒之内事件又被触发,则重新开始计时

       也就是说,如果用户在间隔时间内一直触发函数,那么这个防抖函数内部的真正需要执行的函数将永远无法执行。

       那有没有什么好点的办法,让用户在输入过程中,既能触发真实需要的函数,又能达到优化的效果?

     答案是肯定的,那就是:

      函数截流:规定好一个单位时间,触发函数一次。如果在这个单位时间内触发多次函数的话,只有一次是可被执行的。想执行多次的话,只能等到下一个周期里

  •    截流处理:    
    函数被调用:0
       
    当前时间(分/秒):
  •  

    const count3 = document.getElementById('count3');
    const pt3 = document.getElementById('pt3');
    const time = document.getElementById('time');
    const throttle = (fn, hold = 1000) => {
      let last, deferTimer
      return function(arguments) {
        const _this = this, args = arguments
        let now = +new Date()
        if(last && now < last + hold) {
          clearTimeout(deferTimer)
          deferTimer = setTimeout(function () {
              last = now
              fn.call(_this, args)
            }, hold)
        } else {
           last = now
             fn.call(_this, args)
        }
      }}
    let init3 = 0
    const Ipt = throttle(function() {
    let time = new Date().getMinutes() + ':' + new Date().getSeconds()
        time.innerText = time
       count3.innerText = ++init3
    }, 1000)  // 初始化一下
    
    oIpt3.onkeyup = function() {
      Ipt()
    }

    二、应用场景:防抖和截流都是用来防止高频率的js代码的执行

       1、防抖:防抖本质上就是以最后的操作为标准

    例如:此时此刻我们都在排队等公交,司机说必须等到坐满才会发车,这时候

    的参照标准就是最后一个人上车,公交车好比我们的js代码,最后一个人就充当我们的

    执行条件。例如:

    let setTimer;
    let shake = function() {
      clearTimeout(setTimer);
      setTimer = setTimeout(() => {
        console.log("这里是实际的业务代码");
      }, 0);
    };
    let interTimer = setInterval(() => {
      shake();
    }, 0);
    let timer = setTimeout(() => {
      clearInterval(interTimer);
      clearTimeout(timer);
      timer = null;
      interTimer = null;
    }, 2000);

    执行以上代码,控制台会在 2s 后打出日志,2s 之内的操作都被清空,以最后一次的操作为准。

            如果监听滚动事件,假设两秒以内用户在不断的平凡的触发onScroll事件,只有用户暂停滚动后,才会去执行响应的操作,代码如下

    // 函数防抖
    var timer = false;
    document.getElementById("xxxx").onscroll = function(){
        clearTimeout(timer); // 清除未执行的代码,重置回初始化状态
        timer = setTimeout(function(){
            console.log("函数防抖");
        }, 300);
    };

         2、截流:

             (1)定时器实现截流

    let isAllow = true;
    function shake() {
      let fun = function() {
        if (!isAllow) return;
        isAllow = false;
        let timer = setTimeout(() => {
          console.log("这里是实际的业务代码");
          clearTimeout(timer);
          timer = null;
          isAllow = true;
        }, 1000);
      };
      fun();
    }
    let interTimer = setInterval(() => {
      shake();
    }, 0);
    

       执行以上会看到控制台每隔 1s 后打印出结果,1s 内不会执行打印日志

              (2)闭包实现函数截流:

    // fn是我们需要包装的事件回调, interval是时间间隔的阈值
    function fun(fn, interval) {
        let last = 0; // last为上一次触发回调的时间
        // 将throttle处理结果当作函数返回
        return function() {
          let context = this; // 保留调用时的this上下文
          let args = arguments; // 保留调用时传入的参数
          let now = +new Date(); // 记录本次触发回调的时间
          // 判断上次触发的时间和本次触发的时间差是否小于时间间隔的阈值
          if (now - last >= interval) {
            // 如果时间间隔大于我们设定的时间间隔阈值,则执行回调
            last = now;
            fn.apply(context, args);
          }
        };
      }
      // 用fun来包装scroll的回调
      const better_scroll = fun(() => console.log("触发了滚动事件"), 1000);
      setInterval(() => better_scroll(), 0);
      

    你可能感兴趣的:(JavaScript,javascript)