原生js实现函数防抖和节流

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

 

防抖:前面的所有的触发都被取消,最后一次执行在规定的的时间之后才会触发,也就是说如果连续快速的触发,只会执行一次

通过setTimeout的方式,在一定时间间隔内,将多次触发的变成一次触发

 

    <button>提交</button>
    <script>
     let button=document.querySelector("button")
     button.addEventListener('click',debounce(fun),false)
     //触发函数
     function fun(){  
         console.log(123)
     }
     //防抖函数
      function debounce(fun){
        let time=null
        return function(){
            if(time){
                clearTimeout(time)
            }
            time=setTimeout(function(){
                fun()
             }, 1000);
            
        }
      }
    </script>

 

 

 

 此时就可以实现效果,

但是此时会存在两个问题:

当前的事件对象如何获取,

this指向问题

在没有用防抖函数包装之前

 button.addEventListener('click',fun,false)
     //触发函数
     function fun(e){  
         console.log(this)
         console.log(e)
     }

 可以看到this的指向

 

 

 

 

 

但是当被防抖函数包装之后 

  <script>
     let button=document.querySelector("button")
     button.addEventListener('click',debounce(fun),false)
     //触发函数
     function fun(e){  
         console.log(this,"fun")
         console.log(e,"fun")
     }
     //防抖函数
      function debounce(fun){
       
        let time=null
        return function(e){
            console.log(this,"debounce")
         console.log(e,"debounce")
            if(time){
                clearTimeout(time)
            }
            time=setTimeout(function(){
                fun()
             }, 1000);
            
        }
      }
    </script>

 

 

 如果我们想要调用fun函数的参数

 通过arguments来获取

function debounce(fun){  
        let time=null
        return function(){
            console.log(arguments)
            if(time){
                clearTimeout(time)
            }
            time=setTimeout(function(){
                fun()
             }, 1000);
            
        }
      }

  

 

 

 也就是说,此时输出的第0项,就是事件对象

所以此时这样写

    <script>
     let button=document.querySelector("button")
     button.addEventListener('click',debounce(fun),false)
     //触发函数
     function fun(e){  
         console.log(this,"fun")
         console.log(e,"fun")
     }
     //防抖函数
      function debounce(fun){  
        let time=null
        return function(){
            if(time){
                clearTimeout(time)
            }
            time=setTimeout(()=>{
                // 函数里面不存在arguments对象,所以此时使用箭头函数,用的外层的arguments
                //通过apply方式绑定当前this指向
                fun.apply(null,arguments)
             }, 1000);    
        }
      }
    </script>

 就可以拿到arguments参数问题

 

 

 我们在看this指向的问题

  <script>
     let button=document.querySelector("button")
     button.addEventListener('click',debounce(fun),false)
     //触发函数
     function fun(e){  
         console.log(this,"fun")
         console.log(e,"fun")
     }
     //防抖函数
      function debounce(fun){  
        let time=null
        return function(){
         console.log(this,"debounce")
            if(time){
                clearTimeout(time)
            }
            time=setTimeout(()=>{
                // 函数里面不存在arguments对象,所以此时使用箭头函数,用的外层的arguments
                //通过apply方式绑定当前this指向
                fun.apply(null,arguments)
             }, 1000);    
        }
      }
    </script>

  

 

 

此时输出的this指向

因为我们这里apply绑定了this,所以fun中的this是为空,指向window的

所以此时我们将debounce函数中this通过参数的形式来指向箭头函数外部的this对象

fun.apply(this,arguments)

  

 

 

 此时就可以拿到了,

如果此时我们第一次不想走这个延时

也就是说,第一次点击的时候就立即响应,第二次在进行防抖

 <script>
      let button = document.querySelector("button");
      button.addEventListener("click", debounce(fun, 1000, true), false);
      //触发函数
      function fun(e) {
        console.log(this, "fun");
        console.log(e, "fun");
      }
      //防抖函数
      //第一次点击
      function debounce(fun, timer, trag) {
        let time = null;
        return function () {
          if (time) {
            clearTimeout(time);
          }
          if (trag) {
              //设置判断,如果是第一次就立即执行,将time转换成true
            var first = !time;
            if (first) {
              fun.apply(this, arguments);
            }
            time = setTimeout(() => {
                // 第一次立即执行过去之后将time恢复原样,然后走防抖
              time=null
            }, timer);
          } else {
            time = setTimeout(() => {
              // 函数里面不存在arguments对象,所以此时使用箭头函数,用的外层的arguments
              //通过apply方式绑定当前this指向
              fun.apply(this, arguments);
            }, timer);
          }
        };
      }
    </script>

 

节流:在规定的间隔事件范围内不会重复触发回调,只有大于这个事件间隔才会触发回调,把频繁触发变为少量触发

 <button>提交</button>
    <script>
      let button = document.querySelector("button");
      button.addEventListener("click", debounce(fun, 1000), false);
      //触发函数
      function fun() {
        console.log(123)
      }
      //防抖函数
      //第一次点击
      function debounce(fun, delay) {
          var begin=0;
          return function(){
              var time=new Date().getTime()
              //如果当前的时间减去开始的时间大于设置的延时delay的时候再去执行
              if(time-begin>delay){
                fun.apply(this,arguments)   
                begin=time
              }
          }
       
      }
    </script>

 

posted @ 2021-12-10 21:25  keyeking  阅读(339)  评论(0编辑  收藏  举报