JS 节流、防抖的分享

文章目录

    • 节流、防抖
      • debounce - 防抖
          • 升级版
          • 终极版
          • 防抖应用场景
      • throttle - 节流
          • 时间戳
          • 计时器
          • 合并
          • 防抖应用场景

节流、防抖

前言:要想真正理解节流和防抖,首先了解:this指向、闭包、apply()、计时器等知识。

debounce - 防抖

概念:触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间
当一个事件触发频率很高时,在事件触发的n秒内又触发事件,比如连续点击按钮,就会将等待时间重置。在最后一次事件触发后的 n 秒才执行函数。
比如一个人进入电梯,电梯会等待5s,当不断有人进电梯时,电梯都会重置等待事件,当最后一个人进入电梯的5s后电梯才会关闭。
还比如公交车等乘客,要在最后一个乘客上车后才关门。

官方语法: _.debounce(func, wait, [immediate])

// 最简单的防抖
function debounce(fn,wait){
    var timer = null;
    return function(){ // 闭包
// debounce只会执行一次,后面执行的都是返回的这个函数
        if(timer !== null){
            clearTimeout(timer);
        }
        timer = setTimeout(fn,wait);
    }
}
    
function handle(){
    console.log(Math.random());
}
    
window.addEventListener("resize",debounce(handle,1000));
升级版
// 解决this指向,this应该指向的是当前触发事件的对象
// 解决参数问题,e 指向的是触发事件的对象
// 解决立即执行问题
<body>
  <button id="btn">点击</button>
</body>
<script>
  function debounce(fun,wait,immediate){
    let timeo;
    return function(){
        let context = this;
        let args = arguments;
        clearTimeout(timeo);
        if(immediate){ // 如果为立即执行为true 进入if
          let callnow = !timeo; // 
          timeo = setTimeout(function () {
            timeo = null;
            fun.apply(context,args);
          },wait)
          if(callnow) fun.apply(context,args);
        }else{
            timeo = setTimeout(function(){
            fun.apply(context,args);
        },wait);
        }
    }
}

function handle(){
    console.log(Math.random());
}
// 是否立即执行
let immediate = true;
let btn = document.getElementById('btn');
btn.addEventListener('click',debounce(handle,1000,immediate))
终极版
// 加上可以取消防抖和拥有返回值
<body>
  <button id="btn" class="btn">触发</button>
  <button id="btncl" class="btn">取消</button>
</body>
<script>
  function debounce(fun,wait,immediate){
    let timeo,re;
    let debounce = function(){
        let context = this;
        let args = arguments;
        clearTimeout(timeo);
        if(immediate){
          let callnow = !timeo;
          timeo = setTimeout(function () {
            fun.apply(context,args);
            timeo = null;
          },wait)
          if(callnow) re = fun.apply(context,args);
        }else{
            timeo = setTimeout(function(){
            
            fun.apply(context,args);
        },wait);
      }
      return re;  
    }
    debounce.cancel = function (){
      clearTimeout(timeo);
      timeo = null; // 因为是闭包 所以要置空 解决内存泄漏
    }
    return debounce;
}

function handle(){
    console.log(Math.random());
}
let immediate = false;
let btn = document.getElementById('btn');
let btncl = document.getElementById('btncl');

let result = debounce(handle,3000,immediate);
btn.addEventListener('click',result);
btncl.addEventListener('click',result.cancel);

</script> 

运行截图
JS 节流、防抖的分享_第1张图片

防抖应用场景

应用实例:
搜索框输入查询
按钮提交事件
浏览器窗口的缩放
scroll事件滚动触发

throttle - 节流

概念:指连续触发事件每隔 n 秒执行一次函数。

就像喝水,你一直喝一直喝,但是会每间隔一段时间去上厕所,而不会一直上厕所。

官方语法:_.throttle(function, wait, [options])
options中包含 leading ->开始是否执行 trailing ->结束是否执行
他们只能设置成 true true || false false || true false || false true

时间戳
// 使用时间戳 大于时间周期就执行 第一次触发 最后不会触发
  function throttle(fun,wait){
    let context,args;
    let old = 0;
    return function(){
      context = this;
      args = arguments;
      let now = new Date().valueOf();
      if(now-old > wait){ // 第一次 now-0 是大于wait的 因为now非常的大 可以自行输出一下
          fun.apply(context,args); // 马上执行
          old = now; // 将时间戳记为当下
      }
    }
  }
计时器
// 使用计时器 大于时间周期就执行 第一次不触发 最后会触发 不触头 触尾
  function throttle(fun,wait){
    let context,args,timeo;
    return function(){
      context = this;
      args = arguments;
      if(!timeo){ // 第一次!timeo 为true
        timeo = setTimeout(() => { // timeo得到值,所以在wait时间内都不会进入这个if
          fun.apply(context,args); 
          timeo = null; // 直到wait结束,将!timeo 又变成true
        },wait)
      }
    }
  }

合并
<body>
  <button id="btn" class="btn">触发</button>
  <div></div>
  <button id="btncl" class="btn">取消</button>
</body>
<script>
function throttle(fun,wait,options){
    let context,args,timeo,re;
    let old = 0;
    if(!options) options = {};
    
    let later = function(){
          old = new Date().valueOf(); // 这里的作用就是不让函数进入时间戳的if代码中,而是继续计时wait执行
          re = fun.apply(context,args);
          timeo = null;
    }

    return function(){
      context = this;
      args = arguments;
      let now = new Date().valueOf();
      
      if(options.leading === false && !old){
      // 第一次不执行
        old = now;
      }
      
      if(now-old > wait){ // 第二次点击在wait范围内就不进入
        // 第一次执行必进入
        
        if(timeo){ // 这里的作用是防止还没到计时器的时间就又调用fun
          clearTimeout(timeo);
          timeo = null;
        }
        
        re = fun.apply(context,args);
        old = now;
      }else if(!timeo && options.trailing !== false){
        // 最后一次执行必进入
        timeo = setTimeout(later,wait)
      }
      return re;
    }
  }

  function dothing(){
  console.log(Math.random());
  }

  let btn = document.getElementById('btn');
  let btncl = document.getElementById('btncl');
  btn.addEventListener('click',throttle(dothing,2000,{
    leading:false,
    trailing:true
  }));


</script>
防抖应用场景

应用实例:
飞机大战,飞机在移动,子弹隔一段时间射出
计算鼠标的移动距离

你可能感兴趣的:(笔记,js,javascript)