防抖、节流
当持续高频触发事件,如果回调函数很复杂或者有 Ajax 请求,就会导致响应跟不上触发,出现页面卡顿,假死现象。
防抖
在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。
如下图,持续触发 scroll 事件时,并不执行 handle 函数,当 1000 毫秒内没有触发 scroll 事件时,才会延时触发 scroll 事件。

Preview
应用场景
- 搜索框输入查询
- 窗口大小调整
- 按钮点击
实现原理
简版
用于理解防抖函数原理。
js
function debounce(func, wait) {
let timeout
return function () {
clearTimeout(timeout)
timeout = setTimeout(func, wait)
}
}
完整版
添加对 this
、event
对象的正确解析,以及立刻执行。
js
/**
* @desc 函数防抖
* @param func 函数
* @param wait 延迟执行毫秒数
* @param immediate true 表立即执行,false 表非立即执行
*/
function debounce(func, wait, immediate) {
let timeout
return function () {
const context = this
const args = argments
if (timeoue)
clearTimeOut(timeout)
if (immediate) {
const callNow = !timeout
timeout = setTimeout(() => {
timeout = null
}, wait)
if (callNow)
fnc.apply(context, args)
}
else {
timeout = setTimeout(() => {
func.apply(context, args)
}, wait)
}
}
}
使用 lodash.debounce
例如,下面是一个带有搜索框的页面,并且需要在用户停止输入 500 毫秒后才开始搜索:
js
import { debounce } from 'lodash-es'
// 根据关键词进行搜索
function search(keyword) {
console.log(`Searching for '${keyword}'...`)
}
const inputEl = document.getElementById('search-input')
// 创建 debounce 函数,最多每 500 毫秒执行一次 search 函数
const debouncedSearch = _.debounce(search, 500)
// 监听 input 变化,如果有变化则调用 debouncedSearch 函数
// 每当出现输入变化时,我们会将输入内容传递给 `debouncedSearch` 函数,这个函数会将搜索操作延迟 500 毫秒后执行。因此,只有用户停止输入 500 毫秒之后,才会真正执行搜索操作
inputEl.addEventListener('input', (evt) => {
const keyword = evt.target.value.trim()
debouncedSearch(keyword)
})
节流
节流的原理比较简单,如果你持续触发事件,每隔一段时间,只执行一次事件。
如下图,持续触发 scroll 事件时,并不立即执行 handle 函数,每隔 1000 毫秒才会执行一次 handle 函数。

Preview
应用场景
鼠标移动事件 onmousemove
, 滚动滚动条事件 onscroll
,窗口大小改变事件 onresize
。
实现原理
js
function throttle(fn, wait) {
let start = Date.now()
return function () {
const context = this
const args = arguments
const now = Date.now()
if (now - start > wait) {
fn.apply(context, args)
start = now
}
}
}
使用 lodash.throttle
例如,下面是一个点击按钮时每隔 1 秒输出一次 log 的示例:
js
function log() {
console.log('Clicked!')
}
const btnEl = document.getElementById('click-button')
// 创建 throttle 函数,最多每 1 秒执行一次 log 函数
const throttledLog = _.throttle(log, 1000)
// 监听 click 事件,如果有点击则调用 throttledLog 函数
// 每当用户点击按钮时,我们会调用 `throttledLog` 函数,这个函数会通过限制函数的执行频率,保证每隔 1 秒钟输出一条日志。即使用户连续点击按钮,也只有第一次点击可以触发函数的执行,后续的点击都会被忽略
btnEl.addEventListener('click', (evt) => {
throttledLog()
})