函数防抖、节流

159 阅读2分钟
  • 页面优化:减少DOM操作,防止高频操作DOM造成页面挂掉

我们在平时开发的过程中,会有一些场景:如input框输入内容去调接口,以及onmousemove,resize,onscroll等,正常情况会频繁的去触发事件,造成不必要的性能消耗,为了避免这种情况就会通过函数防抖和函数节流来处理。

函数防抖

  • _.debounce(func, [wait=0], [immediate])

事件响应函数(func)在一段时间(wait)后才会执行,如果在这段时间内再次触发该函数,会重新计算执行时间;当预定的时间内没有再次调用该函数,则执行func。

  • 应用场景
    • scroll时间滚动触发
    • 搜索框输入查询
    • 表单验证
    • 按钮提交
    • 浏览器窗口缩放,resize事件
// 防抖函数
function debounce(func, wait, immediate) {
  let timeout;
  let result;

  let debounced = function() {
    // this指向
    let context = this;
    // event
    let args = arguments;

    clearTimeout(timeout);

    if (immediate) {
      let callNow = !timeout;
      timeout = setTimeout(function() {
        timeout = null;
      },
      wait);

      // 立即执行
      if (callNow) {
        result = func.apply(context, args);
      }
    } else {
      timeout = setTimeout(function() {
        func.apply(context, args);
      },
      wait);
    }

    return result;
  }

  debounced.cancel = function() {
    clearTimeout(timeout);
    timeout = null;
  }

  return debounced;
}

函数节流

  • _.throttle(func, [wait=0], [options])

如果持续触发事件,每隔一段时间,只执行一次

  • 应用场景
    • DOM元素的拖拽功能实现
    • 射击游戏
    • 计算鼠标的移动距离
    • 监听scroll滚动事件
// 节流函数
// 1、使用时间戳节流
// 第一次触发,最后一次不触发
function throttle(func, wait) {
  let context;
  let args;

  // 上一次执行的时间戳
  let old = 0;
  return function() {
    context = this;
    args = arguments;
    // 获取当前时间戳
    let now = Date.now();

    if (now - old > wait) {
      // 立即执行
      func.apply(context, args);
      // 时间戳改变
      old = now;
    }
  }
}

// 2、使用定时器
// 第一次不触发,最后一次触发
function throttle(func, wait) {
  let timeout;
  let context;
  let args;

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

    if (!timeout) {
      timeout = setTimeout(function() {
        timeout = null;
        func.apply(context, args);
      },
      wait);
    }
  }
}

// 3、时间戳和定时器结合
// 第一次触发,最后一次触发
function throttle(func, wait, options) {
  let timeout;
  let context;
  let args;

  // 上一次执行的时间戳
  let old = 0;

  return function() {
    context = this;
    args = arguments;
    // 获取当前时间戳
    let now = Date.now();

    if (!options.leading && !old) {
      old = now;
    }

    if (now - old > wait) {
      // 第一次执行
      if (timeout) {
        clearTimeout(timeout);
        timeout = null;
      }
      // 立即执行
      func.apply(context, args);
      // 时间戳改变
      old = now;
    } else if (!timeout && options.trailing) {
      // 最后一次执行
      timeout = setTimeout(function() {
        old = Date.now();
        timeout = null;
        func.apply(context, args);
      },
      wait);
    }
  }
}