Debounce

防抖函数总结

定义

bounce即弹起,debounce的中文意思为“防抖“

场景

debounce个人理解的使用场景为,某种经常会触发的事件,等待稳定后最后一次性执行。比较典型的有:

  • 键盘输入事件捕捉。等待用户最后输入完成

  • scroll事件,等待用户最后滚动结束处理

  • resize事件,等待用户最后调整结束处理

实现

/// 返回一个函数,但是只要它一直持续地被调用,就不会被触发;除非它最后一次被调用停止了n毫秒
// 如果`immediate`参数被传递,那么会直接执行
function debounce(func, wait, immediate){
  var timeout = null;
  
  return excuteFunction(){
    var context = this;
    var args = arguments;
    var later = function(){
      timeout = null
      if(!immediate) func.apply(context, args);
    }
    
    var callNow = immediate && !timeout;
    if(callNow) func.apply(context, args);
    
    // clearTimeout非常关键。如果在timeout完成后later调用前,excuteFunction又被调用,那么timeou会被清除,并接下来被重新赋值开始
    clearTimeout(timeout)
    
    timeout = setTimeout(later, wait);
  }
}

ES6 实现

function debounceES6(func, wait, immediate) {
  let timeout
  return (...args) => {
    let callNow = immediate && !timeout
    if (callNow) func.apply(this, args);
    
    clearTimeout(timeout);
    
    timeout = setTimeout(() => {
      timeout = null
      if (!immediate) func.apply(this, args)
    }, wait);
  }
}

深度

GitHub有用户专门提到利用箭头函数对于this的处理,省略到context的写法

function debounce (fn, wait) {
  let t
  return function () {
    clearTimeout(t)
    t = setTimeout(() => fn.apply(this, arguments), wait)
  }
}

虽然绝大数情况下箭头函数会调用语义上正确的this,不过考虑如下场景,就会有错误

const obj = {
  name: 'foo',
  sayMyName() {
    console.log('My name is', this.name)
  }
}

obj.sayMyName() //-> My name is foo
obj.deb = debounce(obj.sayMyName, 1000)
obj.deb() // Should log -> My name is foo

在省略context的写法下,只能输出,因为My name isobj的this没有绑定进去。

除非使用

obj.deb = debounce(obj.sayMyName.bind(obj), 1000)
obj.deb() // now -> My name is foo 

最后更新于