【Javascript】 小白必备!手把手教你实现代码节流&防抖?

发布时间:2023年12月25日

代码节流和防抖是前端开发中常用的技巧,可以提高页面性能和用户体验。接下来,我将以“手把手”的方式为你详细介绍如何实现代码节流和防抖。

? 首先,我们来了解一下什么是代码节流和防抖。

代码节流(Throttling)是指降低事件触发频率的一种手段。当一个事件被触发后,在一定时间间隔内,不管事件再次触发多少次,都只会执行一次函数。

代码防抖(Debouncing)与节流相似,但是它允许事件在一定时间间隔内的连续触发,只是等待一段时间后才执行函数。

🔧 接下来,我们开始实现代码节流和防抖。

1?? 首先实现函数节流的工具函数。

函数节流用于限制某些频繁触发的操作(如resizescroll事件)的执行频率。

该函数接收四个参数:delaynoTrailingcallbackdebounceMode,并返回一个新的节流函数。

  • delay: 节流的延迟时间,单位为毫秒。
  • noTrailing: 可选参数,默认为false。如果设为true,表示当节流函数被调用时,每过delay毫秒callback也会执行一次。如果设为false或未传入,则callback将在最后一次调用节流函数后执行一次。
  • callback: 延迟毫秒后执行的函数。在执行节流功能时,callback会按原样传递this上下文和所有参数。
  • debounceMode: 可选参数,默认为false。如果设为true,则clear函数会在delay毫秒后执行;如果设为false,则callback函数会在delay毫秒后执行。

以下是代码:

/**
 * @desc 函数节流。
 * 适用于限制`resize`和`scroll`等函数的调用频率
 * @param {Number} delay 0 或者更大的毫秒数。对于事件回调,建议使用100到250毫秒之间的延迟。
 * @param {Boolean} noTrailing 可选,默认为false。
 *  如果noTrailing为true,当节流函数被调用时,每过`delay`毫秒,`callback`也将执行一次。
 *  如果noTrailing为false或未传入,`callback`将在最后一次调用节流函数后执行一次。
 * @param {Function} callback 延迟毫秒后执行的函数。`this`上下文和所有参数都是按原样传递的,执行节流功能时,调用`callback`。
 * @param {Boolean} debounceMode 如果`debounceMode`为true,`clear`将在`delay`ms之后执行。
 *  如果`debounceMode`为false,`callback`将在`delay`ms之后执行。
 * @return {Function} 新的节流函数
 */
module.exports = function throttle(delay, noTrailing, callback, debounceMode) {
    var timeoutID; // 超时ID,在停止调用包装器后,确保在适当的时间执行`callback`
    var lastExec = 0; // 记录最后一次执行`callback`的时间

    // 如果没有传入`noTrailing`参数,则根据参数顺序重新赋值
    if (typeof noTrailing !== 'boolean') {
        debounceMode = callback;
        callback = noTrailing;
        noTrailing = undefined;
    }

    // `wrapper`函数封装了节流和防抖的功能,当执行该函数时,会限制`callback`的执行频率。
    function wrapper() {
        var self = this;
        var elapsed = Number(new Date()) - lastExec;
        var args = arguments;

        // 执行`callback`并更新`lastExec`时间戳
        function exec() {
            lastExec = Number(new Date());
            callback.apply(self, args);
        }

        // 如果`debounceMode`为true(在开始时),用于清除标记以允许未来的`callback`执行。
        function clear() {
            timeoutID = undefined;
        }

        // 如果处于防抖模式并且没有之前的延迟,执行`callback`
        if (debounceMode && !timeoutID) {
            exec();
        } 

        // 清除任何现有的超时
        if (timeoutID) {
            clearTimeout(timeoutID);
        }

        // 如果处于节流模式,并且超过了延迟时间,则执行`callback`
        else if (debounceMode === undefined && elapsed > delay) {
            exec();
        } 
        
        // 在尾部节流模式下,由于未超过延迟时间,根据最近一次执行后的时间调度`callback`执行
        else if (noTrailing !== true) {
            timeoutID = setTimeout(debounceMode ? clear : exec, debounceMode === undefined ? delay - elapsed : delay);
        }
    }

    // 返回封装函数`wrapper`
    return wrapper;
}; 

2?? 实现函数防抖的工具函数

  • 定义了一个名为?debounce?的函数,该函数接收三个参数:?delay?、?immediate?和 ?callback?。
  • 在函数内部,声明了一个变量 ?timer?,用于存储定时器的引用。
  • 返回一个匿名函数作为实际的防抖函数。
  • 当防抖函数被调用时,将会保存当前的执行上下文和参数。
  • 使用 ?clearTimeout?函数清除之前的定时器。
  • 如果 ?immediate?为 ?true?且之前没有定时器(即首次触发),立即执行回调函数 ?callback?,并将之前保存的执行上下文和参数传递进去。
  • 设定一个新的定时器,在延迟时间 ?delay?之后执行回调函数。
  • 当定时器触发时,将会清空定时器,并检查 ?immediate?是否为 ?false?(即非立即执行模式)。
  • 如果是非立即执行模式,则执行回调函数,并将保存的执行上下文和参数传递进去。

以下是防抖函数的案例:

/**
 * @desc 函数防抖。
 * 适用于限制`resize`和`scroll`等函数的触发频率
 *
 * @param {Number} delay 0 或者更大的毫秒数。对于事件触发,建议使用100到250毫秒之间的延迟。
 * @param {Boolean} immediate 可选,默认为false。
 *  如果immediate为true,在延迟开始前调用,将会立即执行一次该函数。
 *  如果immediate为false或未传入,在延迟结束后调用。
 * @param {Function} callback 延迟毫秒后执行的函数。`this`上下文和所有参数都是按原样传递的,执行防抖功能时,调用`callback`。
 * @return {Function} 新的防抖函数
 */
function debounce(delay, immediate, callback) {
  let timer = null;

  return function () {
    const context = this;
    const args = arguments;

    clearTimeout(timer);

    if (immediate && !timer) {
      callback.apply(context, args);
    }

    timer = setTimeout(function () {
      timer = null;
      if (!immediate) {
        callback.apply(context, args);
      }
    }, delay);
  };
}

使用防抖函数将会在规定的延迟时间内,只执行最后一次触发的函数回调。可以通过设置delay参数来控制延迟的毫秒数,通过设置immediate参数来确定是否立即执行一次函数回调。

标签:#代码节流 #代码防抖 #前端开发 #优化性能

文章来源:https://blog.csdn.net/weixin_44070058/article/details/133694946
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。