真的是每次用的时候都需要查,一直分不清…尴尬的一批

防抖 debounce

在事件触发 ms 之后再执行,在 ms 时间内随意触发事件,都会在这个时间内将之前的方法清除,只会在停止触发 ms 之后才会执行。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
const debounce = (fn, ms) => {
  let timer = null;

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

    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(context, args);
    }, ms);
  };
};

效果可见 codeSandbox

节流 throttle

持续触发事件,每隔一段时间只执行一次方法。可以有两种方法来实现这个效果,时间戳或者是定时器:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 时间戳
const throttle = (fn, ms) => {
  let previous = 0;
  return function (...args) {
    const context = this;
    const now = +new Date();
    if (now - previous > ms) {
      fn.apply(context, args);
      previous = now;
    }
  };
};

// 定时器
const throttleTwo = (fn, ms) => {
  let timer = null;
  return function (...args) {
    const context = this;
    if (!timer) {
      timer = setTimeout(() => {
        clearTimeout(timer);
        timer = null;
        fn.apply(context, args);
      }, ms);
    }
  };
};

时间戳会立即执行方法,但是一旦停下触发事件就不会执行方法。定时器第一次执行是在触发事件 ms 之后,停止触发事件之后还会执行一次方法。

如果想要个有头有尾的呢!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const throttleThree = (fn, ms) => {
  let timeout = null;
  let previous = 0;

  return function (...args) {
    let now = +new Date();
    // 下次触发 func 剩余的时间
    const remaining = ms - (now - previous);
    const context = this;

    // 首次触发remaining会是负
    if (remaining <= 0) {
      if (timeout) {
        clearTimeout(timeout);
        timeout = null;
      }
      previous = now;
      fn.apply(context, args);
    } else if (!timeout) {
      // 这里还是上面用定时器的方法,只是会修改一哈过去的时间
      timeout = setTimeout(() => {
        previous = +new Date();
        clearTimeout(timeout);
        timeout = null;
        fn.apply(context, args);
      }, remaining);
    }
  };
};

效果可见 codeSandbox