参考文章:【NewName】https://juejin.cn/post/7091455593340207134

这一期源码跟着underscore学防抖。

debounce

防抖函数 debounce 指的是某个函数在某段时间内,无论触发了多少次回调,都只执行最后一次。

实现原理

防抖的处理可以通过setTimeout来指定一定时间后执行处理函数,如果在这之前事件再次触发,则清空计时器,重新计时,计时结束后触发函数执行。
基础版本的实现:

  1. debounce(fn, delay = 300) {
  2. let timer;
  3. return function () {
  4. const args = arguments;
  5. if (timer) {
  6. // 清空计时器,重新计时
  7. clearTimeout(timer);
  8. }
  9. timer = setTimeout(() => {
  10. // 改变this指向为调用debounce所指的对象
  11. fn.apply(this, args);
  12. }, delay);
  13. };
  14. }

使用:

  1. window.addEventListener(
  2. "scroll",
  3. debounce(() => {
  4. console.log(111);
  5. }, 1000)
  6. );

underscore 源码实现

写的功能比较全面,在实现的基础上,还包含了立刻执行,立刻执行时有返回值,取消防抖的功能。

  1. 先定位入口函数「debounced」,如果不存在定时器timeout,开始计时,调用later计算计时;
  2. 判断是否需要立刻执行,是 -> 立刻调用
  3. 计时流程:计算经过的时间,与wait比较,如果延时未结束,继续延时,且修改延时的时间;如果延时结束,执行函数(非立刻调用情况),释放 args 和 context 变量(防止内存泄漏)。

    1. export default function debounce(func, wait, immediate) {
    2. // timeout定时器 previous当前时间
    3. var timeout, previous, args, result, context;
    4. // later的作用类似于 if (timer) clearTimeout(timer);
    5. var later = function() {
    6. var passed = now() - previous;
    7. // 当需要等待的时间 > 经过的时间,延时未结束,继续延时。
    8. if (wait > passed) {
    9. // 递归地重新计时,但延时的时长改为 wait - passed,也就是剩余需要等待的时间
    10. timeout = setTimeout(later, wait - passed);
    11. } else {
    12. timeout = null;
    13. // 执行函数(非立刻执行的情况下)
    14. if (!immediate) result = func.apply(context, args);
    15. // This check is needed because `func` can recursively invoke `debounced`.
    16. if (!timeout) args = context = null;
    17. }
    18. };
    19. // restArguments 类似于 es6的「...rest」
    20. var debounced = restArguments(function(_args) {
    21. context = this;
    22. args = _args;
    23. previous = now();
    24. if (!timeout) {
    25. // 开始计时
    26. timeout = setTimeout(later, wait);
    27. // 判断是否立刻调用
    28. if (immediate) result = func.apply(context, args);
    29. }
    30. return result;
    31. });
    32. // 取消防抖,清除定时器
    33. debounced.cancel = function() {
    34. clearTimeout(timeout);
    35. timeout = args = context = null;
    36. };
    37. return debounced;
    38. }

    总结

    underscore实现的功能很完善,可以多学习,手写多几遍,把功能实现都掌握的话,面试很加分。

使用闭包要注意内存泄漏问题。