每天5分钟 - 使用TDD的方式实现防抖

641 阅读2分钟

「这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战」。

前言

大家好,我是轮子猫。本系列文章将带你使用TDD的方式每天花费5分钟,完成一个大厂笔试、面试必考手写题目。

  1. 防抖
  2. 节流
  3. call、apply、bind
  4. EventEmitter
  5. 手动实现ES5继承
  6. 手动实现instanceof
  7. Object.create
  8. new
  9. 浅拷贝和深拷贝
  10. 单例模式
  11. 手动实现JSONP
  12. 数组去重、扁平、最值
  13. 数组乱序 - 洗牌算法
  14. 函数柯里化
  15. 模拟实现promise
  16. 基于Promise的ajax封装
  17. 异步循环打印
  18. 图片懒加载

需求

在实现防抖之前,我们先来了解一下防抖的定义。防抖指的是,不管事件触发频率有多高,一定事件触发n秒后才执行。如果你在一个事件触发的n秒内又触发了这个事件,就以新的事件的时间为准,n秒后才执行并取消前面触发的事件。 ​

现在我们来拆解一下需求:

  1. 函数触发n秒后才执行,也就是延迟执行函数
  2. 函数在n秒被连续触发,前面被触发等待的函数,都取消执行。并且重新以这次的触发事件时间为准,延迟执行该函数。

实现

现在我们来一步一步实现防抖。 ​

需求1

首先,我们先实现关于延迟执行函数的测试代码

// debounce.test.js
test('should dealy function', function(done) {
  const mockFn = jest.fn();
  const debouncedFn = debounce(mockFn, 10);
  
  debouncedFn();
  expect(mockFn.toBeCalledTimes(0);
         
  setTimeout(function() {
    expect(mockFn).toBeCalledTimes(1);
    
    done();
  }, 20);
})

运行测试代码

image.png

实现测试代码

// debounce.js
function debounce(fn, n) {
  return function () {
    setTimeout(function () {
      fn();
    }, n);
  };
}

接着,我们继续运行测试代码,OK

image.png

需求2

现在,我们来实现拆解后的需求2,测试代码如下

// debounce.test.js
test('should debounce function', function() {
  const mockFn = jest.fn()
  const debouncedFn = debounce(mockFn, 10);
  
  debouncedFn()
  debouncedFn() 

  expect(mockFn).toBeCalledTimes(0);
  
  
  setTimeout(function () {
      expect(mockFn).toBeCalledTimes(1);

      done();
  }, 20);
})

实现

// debounce.js
function debounce(fn, n) {
  let prev = 0
  let id
  
  return function (...args) {
    // 1. 如果函数调用的时间在n秒内:这次函数开始调用的时间 - 上一次函数开始设置定时器的时间
    const now = Datw.now()
    if (id && now - prev <= n)  {
      // 2. 取消刚刚的函数调用
      clearTimeout(id)
    }      
   
    // 3. 以新的调用事件为准,n秒后才执行
    prev = Date.now()
    
    id = setTimeout(function () {
      fn(...args);
    }, n);
  };
}

最后,运行测试代码

image.png

这样,最基本的防抖函数就已经成功实现了,收工!~