【防抖、节流】

1、防抖

/***
 * 防抖 ,在规定的时间里触发多次事件,只有最后一次会被执行
 * @param fun 要执行的函数
 * @param wait 延时
 * @param immediate 是否立即执行
 */
function debounce(fun, wait, immediate) {
  let timer, result;

  let debounced: any = function () {
    let context = this; // 解决this指向
    let args = arguments; // 解决event对象
    if (timer) clearTimeout(timer);
    if (immediate) {
      // 如果没有已经已经创建的定时器,就立刻执行 ,并将函数执行结果存进result中
      if (!timer) result = fun.apply(context, args);
      // 执行完后,就清除定时器
      timer = setTimeout(() => {
        timer = null;
      }, wait);

    } else {
      timer = setTimeout(fun.apply(context, args), wait)
    }

    return result;
  }

  // 如果需要取消防抖
  debounced.cancel = function(){
    clearTimeout(timer);
    timer = null
  }
  return debounced
}

// 用法,比如鼠标移动事件
var count = 1;
var container = document.getElementById('container');

function getUserAction(e) {
  container.innerHTML = count++;
};

// 在这里使用防抖
var setUseAction = debounce(getUserAction, 10000, true);

container.onmousemove = setUseAction;

// 点击button的话,就会取消防抖
document.getElementById("button").addEventListener('click', function(){
  setUseAction.cancel();
});

2、节流

// 节流,只能在规定的时间内触发一次事件, 使用时间戳
function throttle1(func, wait){
  let previous = 0;

  return function(){
    let now = +new Date();
    console.log('now', now)
    let context = this;
    let args = arguments;

    if(now - previous > wait){
      func.apply(context, args);
      previous = now;
    }
  }
}

// 节流,使用定时器
function throttle2(func, wait){
  let timeout;

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

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

// 事件执行立刻触发,事件结束也会立刻执行一次
/**
 * 节流, 只能在规定的时间内触发一次事件,
 * @param func 要执行的函数
 * @param wait
 * @param options leading:false 表示禁用第一次执行 trailing: false 表示禁用停止触发的回调
 */
function throttle(func, wait, options){
  let timeout,context, args;

  let previous = 0;

  let later = function(){
    previous = options.loading ?  +new Date() : 0;
    timeout = null;
    func.apply(context, args);
    if(!timeout)context = args = null;
  }

  let throttled = function(){
    context = this;
    args = arguments;

    let now = +new Date();
    if(!previous && !options.loading) previous = now;
    let remaining = wait - (now - previous);
    // 如果没有剩余时间了,或者改了系统时间
    if(remaining <= 0 || remaining > wait){
      if(timeout){
        clearTimeout(timeout);
        timeout = null;
      }
      previous = now;
      func.apply(context, args);
      if(!timeout) context = args = null
    } else if(!timeout && !options.trailing){
      timeout = setTimeout(later, remaining)
    }
  }

  return throttled;
}
//用法
// 我们要注意 underscore 的实现中有这样一个问题:
//
// 那就是 leading:false 和 trailing: false 不能同时设置。
//
// 如果同时设置的话,比如当你将鼠标移出的时候,因为 trailing 设置为 false,停止触发的时候不会设置定时器,所以只要再过了设置的时间,再移入的话,就会立刻执行,就违反了 leading: false,bug 就出来了,所以,这个 throttle 只有三种用法:
container.onmousemove = throttle(getUserAction, 1000);
container.onmousemove = throttle(getUserAction, 1000, {
  leading: false
});
container.onmousemove = throttle(getUserAction, 1000, {
  trailing: false
});

你可能感兴趣的:(前端)