函数防抖和节流

73 阅读2分钟

有时候需要绑定一些持续触发的事件,如 resize、scroll、mousemove 等等,但有些时候我们并不希望在事件持续触发的过程中那么频繁地去执行函数。

一、防抖

所谓防抖,就是指触发事件后 n 秒后才执行函数,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。

防抖函数分为非立即执行版和立即执行版。

① 非立即执行版:

function debounce (func, wait) {
    let timeout;
    return function () {
        const context = this;
        const args = [...arguments]
        if (timeout) clearTimeout(timeout);
        timeout = setTimeout(() => {
            func.apply(context, args)
        }, wait);
    }
}

在 n 秒后执行,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。

防抖函数的代码还需要注意的是 this 和 参数的传递.

const context = this;
const args = [...arguments];

是为了让 debounce 函数最终返回的函数 this 指向不变以及依旧能接受到 e 参数。

② 立即执行版:

function debounce (func, wait) {
    let timeout;
    return function () {
        const context = this
        const args = [...arguments]
        if (timeout) clearTimeout(timeout)
        const callNow = !timeout;
        timeout = setTimeout(() => {
            timeout = null;
        }, wait)
        if (callNow) func.apply(context, args)
    }
}

③ 合并立即非立即:

/**
 * @desc 函数防抖
 * @param func 函数
 * @param wait 延迟执行毫秒数
 * @param immediate true 表立即执行,false 表非立即执行
 */
function debounce(func, wait, immediate) {
  let timeout;
  return function () {
    const context = this;
    const args = [...arguments];
    if (timeout) clearTimeout(timeout);
    if (immediate) {
      const callNow = !timeout;
      timeout = setTimeout(() => {
        timeout = null;
      }, wait)
      if (callNow) func.apply(context, args)
    }
    else {
      timeout = setTimeout(() => {
        func.apply(context, args)
      }, wait);
    }
  }
}

节流

所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。

节流会稀释函数的执行频率。

对于节流,一般有两种方式可以实现,分别是时间戳版和定时器版。

① 时间戳版:

function throttle(func, wait) {
    var previous = 0;
    return function () {
        let now = Date.now()
        let context = this
        let args = arguments
        if (now - previous > wait) {
            func.apply(context, args);
            previous = now;
        }
    }
}

② 定时器版

function throttle(func, wait) {
    let timeout
    return function () {
        let context = this
        let args = arguments
        if (!timeout) {
            timeout = setTimeout(() => {
                timeout = null
                func.apply(context, args)
            }, wait)
        }
    }
}