极简式节流与防抖

相信大家对节流和防抖这两个词都不陌生,也大概知道一点是为了减少函数的频繁执行,但对于它们的区别、实现方式以及使用场景估计有点搞不清。

今天我就用极简的白描手法带大家彻底弄清楚节流与防抖。

频繁执行的函数

来看个输入框的例子:

let $input1 = document.querySelector('#input1')

function func (content) {
  console.log(content, new Date())
}

$input1.addEventListener('input', function (e) {
  func(e.target.value)
})

可以看到,只要按下键盘就会触发input事件,就会执行一次func函数。假如这是一个搜索框,那么用户每输入一个字符就会请求一下服务器,而且如果输入的是中文,还没等用户输入完整的拼音就会请求很多次,这无疑是非常浪费资源的。下面我们用防抖和节流解决下这个问题。

防抖(debounce)

事件停止触发n秒后再执行函数,如果在这n秒内又被触发,则重新计时。

// 防抖:事件停止触发n秒后再执行函数,如果在这n秒内又被触发,则重新计时。
function debounce(func, delay) {
  let timer = 0
  return (...args) => { // 将函数的所有参数收敛到args数组中
    clearTimeout(timer)
    timer = setTimeout(() => {
      // 将参数全部传给func
      func(...args)
    }, delay)
  }
}

let $input2 = document.querySelector('#input2')

function func (content) {
  console.log(content, new Date())
}

let debounceFunc = debounce(func, 500)
$input2.addEventListener('input', function (e) {
  debounceFunc(e.target.value)
})

可以看到,用防抖函数处理后输入内容时并不会立即执行func函数,而是等用户停止输入500毫秒后再执行,如果还没过500毫秒又输入了,则会清空计时器,重新计时。

节流(throttle)

节流这个词怎么理解呢?举个栗子,把一个出水很大的水龙头关小,水还是会流,只是流的慢了,这就是节流了。

在一定时间间隔内,只执行一次函数

// 节流:在一定时间间隔内,只执行一次函数
function throttle (func, delay) {
  let lastTime = 0 // 上一次执行函数的时间
  let timer = 0
  return (...args) => {
    let now = new Date()
    clearTimeout(timer)
    if (lastTime && now - lastTime >= delay) {
      func(...args)
      lastTime = now
    } else {
      timer = setTimeout(() => { // 保证最后一次能执行
        func(...args)
        lastTime = now
      }, delay)
    }
  }
}

let $input3 = document.querySelector('#input3')

function func (content) {
  console.log(content, new Date())
}

let throttleFunc = throttle(func, 1000)
$input3.addEventListener('input', function (e) {
  throttleFunc(e.target.value)
})

可以看到用节流函数处理后,每隔1s执行一次func函数,第一次和最后一次都会执行。

总结

应用场景

把防抖和节流的原理搞明白了,什么场合下使用就非常简单了,列举几个简单的场景:

查看demo