implement debounce with JavaScript

51 阅读1分钟

手写防抖 使用点击按钮事件模拟

// step1
// 模拟按钮被点击
let addBtn = document.getElementById('add');

function addOne() {
  console.log('add one!');
}

addBtn.addEventListener('click', addOne);

// step2
// 封装addOne
function debounceSimple(fn) {
  return function () {
    fn();
  };
}

addBtn.addEventListener('click', debounceSimple(addOne));

// step3
// 添加延时器
// notice: 现在延时的目的是达到了但是每次点击都会新增一个新的setTimeout而且并不能达到我们多次点击只执行一次的效果
function debounceWithTimer(fn, time) {
  return function () {
    setTimeout(() => {
      fn();
    }, time);
  };
}

addBtn.addEventListener('click', debounceWithTimer(addOne, 2000));

// step4
// 引入clearTimeout,清除之前的timer并重新进行倒计时
function debounceWithClearTimeout(fn, time) {
  let timer;

  return function () {
    clearTimeout(timer); // 达到重新计时的目的

    timer = setTimeout(() => {
      fn();
    }, time);
  };
}

addBtn.addEventListener('click', debounceWithClearTimeout(addOne, 2000));

// step5
// 👆基本的防抖功能已经实现
// but
// 在addOne()里面打印this,指向的是当前的运行环境window
// 使用apply改变this指向
// 并且需要考虑到需要防抖的函数,有传入的参数 => 使用arguments处理
function debounce(fn, time) {
  let timer = null;

  return function () {
    clearTimeout(timer);
    let args = arguments;

    timer = setTimeout(() => {
      fn.apply(this, args);
    }, time);
  };
}

addBtn.addEventListener('click', debounce(addOne, 2000));

// final version
// 优化
function debounceOptimized(fn, wait) {
  let timer = null;

  return function () {
    let ctx = this,
      args = arguments;

    if (timer) {
      clearTimeout(timer);
      timer = null;
    }

    timer = setTimeout(() => {
      fn.apply(ctx, args);
    }, wait);
  };
}

addBtn.addEventListener('click', debounceOptimized(addOne, 2000));