返回

TDD实践:每天5分钟防抖一记,手把手教你实现防抖防闪

前端

前言

各位技术爱好者,大家好!我是技术探索领域的领航者,wheelcat。今天,我们踏上了一段TDD实践之旅,每天仅需5分钟,我们将深入浅出地剖析大厂笔试和面试中的必备手写题目。而今天,我们的目标是征服防抖,实现手写防抖防闪的壮举!

防抖的奥义

防抖,顾名思义,就是在频繁触发的事件中,仅在满足一定时间间隔后才执行响应函数。其精髓在于抑制快速、连续的事件触发,确保在一定时间窗口内只执行一次响应函数。

防抖实现实战

让我们用TDD的方式,一步步实现防抖功能。

Step 1:定义需求

type DebouncedFunction = (this: any, ...args: any[]) => any;

// 防抖函数接口
interface DebounceOptions {
  // 防抖时间间隔,单位毫秒,默认为0,表示不防抖
  wait?: number;
}

// 防抖函数
function debounce(func: DebouncedFunction, options?: DebounceOptions): DebouncedFunction;

Step 2:编写测试用例

describe('debounce', () => {
  it('should delay function execution', () => {
    const spy = jest.fn();
    const debounced = debounce(spy, { wait: 100 });
    debounced();
    expect(spy).not.toHaveBeenCalled();
  });

  it('should execute function after wait period', () => {
    const spy = jest.fn();
    const debounced = debounce(spy, { wait: 100 });
    debounced();
    setTimeout(() => {
      expect(spy).toHaveBeenCalledTimes(1);
    }, 100);
  });

  it('should execute function only once within wait period', () => {
    const spy = jest.fn();
    const debounced = debounce(spy, { wait: 100 });
    debounced();
    debounced();
    setTimeout(() => {
      expect(spy).toHaveBeenCalledTimes(1);
    }, 100);
  });
});

Step 3:实现防抖函数

function debounce(func: DebouncedFunction, options?: DebounceOptions): DebouncedFunction {
  let timeoutId: number | null = null;

  return function (this: any, ...args: any[]) {
    if (timeoutId) {
      clearTimeout(timeoutId);
    }

    timeoutId = setTimeout(() => {
      func.apply(this, args);
      timeoutId = null;
    }, options?.wait ?? 0);
  };
}

Step 4:验证测试用例

运行测试用例,确保所有断言均通过。

恭喜!你已经成功实现了手写防抖函数。

防闪优化

防闪,是防抖的一种特殊情况,适用于连续触发事件的情况。在防闪场景下,当触发事件的频率高于防抖时间间隔时,响应函数只会在事件停止触发后的指定时间间隔内执行一次。

防闪实现实战

Step 1:修改需求

// 防闪选项
interface DebounceWithLeadingOptions extends DebounceOptions {
  // 是否开启防闪,默认为false,即不开启
  leading?: boolean;
}

// 防闪函数
function debounce(func: DebouncedFunction, options?: DebounceWithLeadingOptions): DebouncedFunction;

Step 2:编写测试用例

describe('debounce', () => {
  it('should execute function immediately if leading is true', () => {
    const spy = jest.fn();
    const debounced = debounce(spy, { leading: true, wait: 100 });
    debounced();
    expect(spy).toHaveBeenCalledTimes(1);
  });

  it('should execute function only once within wait period even with leading', () => {
    const spy = jest.fn();
    const debounced = debounce(spy, { leading: true, wait: 100 });
    debounced();
    debounced();
    setTimeout(() => {
      expect(spy).toHaveBeenCalledTimes(1);
    }, 100);
  });
});

Step 3:实现防闪

在原有防抖函数的基础上进行修改:

function debounce(func: DebouncedFunction, options?: DebounceWithLeadingOptions): DebouncedFunction {
  let timeoutId: number | null = null;
  let leadingCall = options?.leading ?? false;

  return function (this: any, ...args: any[]) {
    if (timeoutId) {
      clearTimeout(timeoutId);
    }

    if (leadingCall) {
      func.apply(this, args);
      leadingCall = false;
    }

    timeoutId = setTimeout(() => {
      func.apply(this, args);
      timeoutId = null;
    }, options?.wait ?? 0);
  };
}

Step 4:验证测试用例

运行测试用例,确保所有断言均通过。

太棒了!你已经成功实现了手写防闪函数。

进阶思考

防抖和防闪是前端开发中的常见技术,广泛应用于各种场景。掌握防抖和防闪技巧,不仅能提升代码质量,还能提升用户体验。

在实际应用中,可以根据需要灵活选择是否开启防闪,并调整防抖时间间隔,以达到最佳效果。

欢迎在评论区分享你的经验和心得,与其他技术爱好者共同探索技术奥秘!