js防抖与节流

1,513 阅读2分钟

前言

在网页中提交表格时,常常有用户疯狂点击表单提交,导致重复发送的网络请求;在交互相关的动画中,有些场景通过mousemove监听,但是若每一秒钟都触发函数会降低网页性能。

以上两个举例正是防抖(debounce)与节流(throttle)的所对应场景解决的问题。

本文描述何为防抖(debounce)与节流(throttle),并如何用原生实现它们。当然,想要现成的函数在lodash中已经有了——lodash - debouncelodash - throttle

  • 防抖(debounce):把多个连续重复的调用合并为一个,仅调用一次函数。
  • 节流(throttle):节流会延迟函数执行,减少事件触发的响应。

可在此网站的可视化中了解它们的区别:点击这里

接下来描述防抖与节流的原生实现。原生实现学习于函数防抖与函数节流 - 知乎 - 司徒正美,在此基础上增添注释与理解。

防抖

防抖的原理是使用了闭包,通过不断地记录新的定时器与清除旧的定时器,达到函数调用不断延迟,且仅调用一次的效果。

function debounce(func, delay){
  let timeout;//记录定时器
  return function(e){
    console.log("清除",timeout, e.target.value);
    clearTimeout(timeout);
    const context = this;
    const args = arguments;
    timeout = setTimeout(function(){
      console.log("———");
      func.apply(context, args);
    },delay);   
    console.log("新的",timeout, e.target.value);
  };
};

const validate = debounce(function(e){
  console.log("验证函数触发!", e.target.value, new Date())
}, 300);

document.querySelector('input').addEventListener('input', validate);

CodePen打开

节流

节流的原理也是使用了闭包,记录了开始时间start及每一次触发时间curr,两变量相减得到差值,当插值大于节流分片的时间时,可执行函数,同时更新start。另外通过setTImeout防止函数持续触发时间太短,没有调用函数,对于定时器也有所每次清除。

function throttle(fun, thresholdTime){
  let timeout;
  let start = new Date();
  let threshold = thresholdTime || 160;
  return function(){
    const context = this;
    const args = arguments;
    const curr = new Date();
    
    console.log("清除timeout")
    clearTimeout(timeout);
    
    if(curr - start >= threshold){//间隔时间到则可执行函数
      console.log("间隔时间", curr-start);
      fun.apply(context, args);
      start = curr; //执行完毕,更新开始时间
    }else{
      //在方法在离开事件时也能执行一次,防止持续触发时间很短,导致方法无法触发。
      timeout = setTimeout(function(){
        fun.apply(context, args)
      }, threshold);
    }
  }
}

const mousemove = throttle(function(e){
  console.log("mousemove函数触发");
},300);

document.querySelector('#test').addEventListener('mousemove',mousemove);

CodePen打开

总结

实现一遍防抖与节流的封装后并不难理解它们的思想,主要是利用了闭包保存创建时的环境的特性,记录变量已完成函数是否执行的判断。

参考资料

函数防抖与函数节流 - 知乎 - 司徒正美
lodash - debounce
lodash - throttle
Difference Between throttling and debouncing a function - StackOverflow
防抖节流可视化