防抖和节流实现原理

349 阅读2分钟

前言

防抖和节流是面试常问的热点。如果能够很好的理解防抖和节流的要点,确实可以说明面试者具备了一定的前端知识。

需要注意的三个点

  • 闭包 防抖和节流函数本质都是闭包函数。闭包函数返回了一个函数,返回函数可以调用闭包环境中的变量。
// 防抖或节流,以防抖函数为例
function debounce(delay, callback){
    // 闭包环境中的变量
    let lastTime = 0;
    // 闭包中的返回函数
    return function (){
       lastTime = Date.now()
       ...
    }
}
  • 延时执行 延时执行一般通过定时器实现。其中节流也可以通过时间戳实现。
// 时间戳 currentTime, lastTime
function throttle(delay, callback) {
    let lastTime = 0;
    return function () {
        // 获取当前时间
        const currentTime = new Date.now(); 
        if (currentTime - lastTime > delay) { 
            ...
            lastTime = currentTime; 
        } 
    };
}

// 定时器
function throttle(delay, callback) {
    let timer;
    return function () {
        if (timer) return 
        timer = setTimeout(()=>{
             ...
             
             timer = null
        }, delay)
       
         lastTime = currentTime; 
        
    };
}
  • 函数形参 arguments 和绑定 this 防抖和节流函数在绑定 js 事件,触发后,参数需要传递给 callback 函数,并且需要绑定 this。其中如果使用了箭头函数,需要注意箭头函数没有this 和arguments。 this 为箭头函数所在的执行上下文中的 this, arguments 为箭头函数所在父函数的 arguments。
// 以节流函数为例
function throttle(delay, callback){
    ...
    return function(){
        // this 指的js 事件触发时的执行环境;arguments 为事件回调函数的参数
        callback.apply(this, arguments)
    }
}

自己实现的防抖和节流函数

// 防抖
function debounce(delay,callback){
    let timer
    return function(){
        clearTimeout(timeout)
        timer = setTimeout(() => {
          callback.apply(this, arguments)
        }, delay)
    }
}


// 节流-定时器版
function throttle(delay, callback) {
    let flagExec = true
    return function () {
      if (!flagExec) {
        return
      }
      flagExec = false
      setTimeout(() => {
        callback.apply(this, arguments)
        flagExec = true
      }, delay)
   }
}

// 节流-时间戳版
function throttle(delay, callback) {
    let lastTime = 0;
    return function () {
        // 获取当前时间
        const currentTime = Date.now(); 
        if (currentTime - lastTime > delay) { 
            callback.apply(this, arguments)
            lastTime = currentTime; 
        } 
    };
}


// 调用,本质上是需要执行一次 debounce 和 throttle 函数
document.onmousemove = debounce(1000, function(args){console.log(123)});
document.onmousemove = throttle(1000, function(args){console.log(123)});

进阶

防抖和节流还可以功能更丰富一些,比如:取消,事件触发立即执行一次,停止触发再执行一次等。 有兴趣的可以翻看 throttle-debounce 库源码, 这里不再赘述。

其他

欢迎大家关注微信公众号:赵公子聊前端