函数节流与函数防抖

2020-07-21

函数节流(throttle)

函数节流:在指定的间隔时间内只执行一次
有个需要频繁触发函数,出于优化性能角度,在规定时间内,只让函数触发的第一次生效,后面不生效。
比如下面的例子,在不加函数节流的时候,每当滚动条滚动的时候都会触发一次,造成大量的性能浪费

// 未添加节流函数
document.onscroll = function () {
  console.log('scroll事件被触发了')
}

添加了节流函数后

// 添加了节流函数
document.onscroll = throttle(function () {
  console.log('scroll事件被触发了')
}, 300)


具体代码实现

/**
 * @description 函数节流
 * @param {Function} fn 需要执行函数节流的函数
 * @param {Number} interval 指定间隔时间
 */
function throttle(fn, interval = 300) {
  let canRun = true // 通过闭包保存一个标记
  return function () {
    if (!canRun) return // 第一次调用执行
    canRun = false // setTimeout未执行时,后续fn函数调用都不会再执行
    // setTimeout 定时器延时执行
    setTimeout(() => {
      fn.apply(this, arguments)
      canRun = true // 标记为true,节流完成
    }, interval)
  }
}

代码解释
简单来说,函数的节流就是通过闭包保存一个标记(canRun = true), 在函数的开头判断这个标记是否为true,如果这个标记为true的话就继续执行,否则就return掉,判断完标记后立即把这个标记设置为false,然后把外部传入的函数的执行包在一个setTimout中,最后在定时器执行完毕之后再把标记设置为true,表示本次延迟执行完毕,可以执行下一次循环了。当定时器还未执行完毕的时候,canRun这个标记始终未false,故在开头的判断中总是被return掉,函数并未执行。、
应用场景
监听滚动事件判断是否到页面底部自动加载更多:给 scroll 加了 debounce 后,只有用户停止滚动后,才会判断是否到了页面底部;如果是 throttle 的话,只要页面滚动就会间隔一段时间判断一次等

函数防抖(debounce)

函数防抖: 一个需要频繁触发的函数,在规定时间内,只让最后一次生效,前面的不生效。
比如点击一个按钮,每点击一次就会触发一次事件,在没有加防抖函数的情况下,快速点击会导致多次触发

// 未加防抖函数
document.getElementById('btn').onclick = function(){
  console.log('我被点击了');
}

在加了防抖函数后,只会在规定时间后触发一次

// 加了防抖函数
document.getElementById('btn').onclick = debounce(function(){
  console.log('我被点击了');
},300)

具体代码实现

/**
 * @description 函数防抖
 * @param {Function} fn 需要执行函数防抖的函数
 * @param {Number} interval 指定间隔时间
 */
function debounce(fn, interval = 300) {
  let timeout = null // 通过闭包保存一个标记
  return function () {
    clearInterval() // 把前一个定时器去掉
    // 又创建一个新的定时器
    timeout = setTimeout(() => {
      fn.apply(this, arguments) // 指定的时间间隔之后运行fn
    }, interval)
  }
}

代码解释
其原理就第一次调用函数,创建一个定时器,在指定的时间间隔之后运行代码。当第二次调用该函数时,它会清除前一次的定时器并设置另一个。如果前一个定时器已经执行过了,这个操作就没有任何意义。然而,如果前一个定时器尚未执行,其实就是将其替换为一个新的定时器,然后延迟一定时间再执行。
应用场景
文本输入的验证(连续输入文字后发送 AJAX 请求进行验证,验证一次就好)