javaScript简单实现 函数节流与防抖

151 阅读2分钟

函数节流(throttle.js)

/**
 * @函数节流
 * @param func 函数
 * @param wait 多长时间间隔执行一次函数
 * @param options 配置选项
 *      @param leading 第一次是否执行
 *      @param trailing 最后一次是否执行
 */
var throttle = function (func, wait, options) {
  var context, args, result; // 上下文this, 函数func的参数, 函数func的执行结果
  var timeout = null; // 定时器
  var previous = 0; // 上次事件func的执行时间毫秒数
  if (!options) options = {}; // 不传选项时进行重置
  var later = function () {
    // 如果 options.leading 为 false , 上次事件执行时间赋值为0
    previous = options.leading === false ? 0 : Date.now();
    timeout = null; // 重置timeout
    result = func.apply(context, args); // 传递上下文参数并执行
    if (!timeout) context = args = null; // 释放变量
  };
  // 返回一个函数
  return function () {
    context = this; // 保存上下文
    args = arguments; // 保存参数
    var now = Date.now(); // 当前毫秒数
    // 更新执行func的时间previous,并禁用func第一次首先执行
    if (!previous && options.leading === false) previous = now;
    // 还剩下多少延迟时间
    var remaining = wait - (now - previous);

    // remaining <= 0已经足够证明已经到达wait的时间间隔,但这里还考虑到假如客户端修改了系统时间则马上执行func函数。
    if (remaining <= 0 || remaining > wait) {
      // 如果定时器存在,清除定时器
      if (timeout) {
        clearTimeout(timeout);
        timeout = null;
      }
      // 将当前时间重新赋值给previous
      previous = now;
      // 执行函数func,将结果保存在result
      result = func.apply(context, args);
      if (!timeout) context = args = null; // 释放变量
    } else if (!timeout && options.trailing !== false) {
      timeout = setTimeout(later, remaining);
    }
    return result;
  };
};

函数防抖(debounce.js)

/**
 * @函数防抖
 * @param func 函数
 * @param wait 时间间隔 wait秒内触发不会重新执行函数
 * @param immediate 是否立即执行
 */
var debounce = function (func, wait = 1000, immediate) {
  // 定时器,函数func的参数,上下文this, 上一个函数被返回的时间戳, 函数func的执行结果
  var timeout, args, context, timestamp, result;
  // 这是一个定时器任务函数(可以先不看这个,看完下面的代码再来看这个函数)
  var later = function () {
    // 该定时器时间生成的时间戳与上一函数的时间戳的差值
    var last = Date.now() - timestamp;
    // 差值在0~wait之间,就重新设置定时器,否则判断是否已经执行过一次了,没有执行过就执行一次该函数
    if (last < wait && last >= 0) {
      timeout = setTimeout(later, wait - last);
    } else {
      timeout = null;
      if (!immediate) {
        result = func.apply(context, args);
        if (!timeout) context = args = null; // 释放变量
      }
    }
  };
  return function () {
    context = this; // 保存上下文对象
    args = arguments; // 保存参数
    timestamp = Date.now(); // 保存当前时间戳
    // 获取立即执行的判定值,如果用户设定立即执行,并且之前没有定时器
    var callNow = immediate && !timeout;
    // 设置定时器
    if (!timeout) timeout = setTimeout(later, wait);
    // 如果符合立即执行的条件就马上执行一次函数
    if (callNow) {
      result = func.apply(context, args);
      context = args = null; // 释放变量
    }
    return result;
  };
};

测试(index.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>节流防抖</title>
    <style>
        #root{
            width: 50px;
            height: 50px;
            background-color: aqua;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <button id="root"></button>
    <!-- <script src="./throttle.js"></script> -->
    <script src="./debounce.js"></script>
    <script>
        function logger(){
            console.log('logger')
        }
        root.addEventListener('click', debounce(logger, 1000, false))
        // root.addEventListener('click', throttle(logger, 2000, {leading: true, trailing: true}))
    </script>
</body>
</html>