防抖与节流函数
2022年10月14日大约 3 分钟
防抖和节流的作用都是在高频事件中防止函数被多次调用,是一种性能优化的方案
区别
- 防抖函数:只会在高频事件结束后 n 毫秒调用一次函数
- 节流函数:会在高频事件触发过程当中每隔 n 毫秒调用一次函数。
防抖函数
触发高频事件后一段时间(wait)只会执行一次函数,如果指定时间(wait)内高频事件再次被触发,则重新计算时间。
// 防抖函数
function debounce(func, wait) {
let timeout = null
return function () {
let context = this
let args = arguments
if (timeout) clearTimeout(timeout)
timeout = setTimeout(() => {
func.apply(context, args)
}, wait)
}
}
节流函数
规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效
// 节流函数
function throttle(func, wait) {
let timeout = null
return function () {
let context = this
let args = arguments
if (!timeout) {
timeout = setTimeout(() => {
timeout = null
func.apply(context, args)
}, wait)
}
}
}
应用场景
常见的应用场景都是使用高频事件来调用函数的过程当中,比如应用于 window 对象的 resize、scroll 事件,拖拽时的 mousemove 事件,文字输入、自动完成的 keyup 事件。
防抖应用场景
- scroll 事件滚动触发事件
- 搜索框输入查询,如果用户一直在输入中,没有必要不停地调用去请求服务端接口,等用户停止输入的时候,再调用,设置一个合适的时间间隔,有效减轻服务端压力。
- 表单验证
- 按钮提交事件。
- 浏览器窗口缩放,resize 事件(如窗口停止改变大小之后重新计算布局)等。
节流的应用场景
- DOM 元素的拖拽功能实现(mousemove)
- 搜索联想(keyup)
- 计算鼠标移动的距离(mousemove)
- Canvas 模拟画板功能(mousemove)
- 射击游戏的 mousedown/keydown 事件(单位时间只能发射一颗子弹)
- 监听滚动事件判断是否到页面底部自动加载更多
示例
<p>
说明:鼠标在以下元素不断移动,将会不断执行一个数值累加事件,但中间分别加入了防抖和节流函数。
</p>
<h2>防抖</h2>
<p>在鼠标停止移动后300ms执行一次数值累加事件。</p>
<div id="content">0</div>
<h2>节流</h2>
<p>在鼠标移动过程中,每300ms执行一次数值累加事件。</p>
<div id="content2">0</div>
div {
height: 150px;
line-height: 150px;
text-align: center;
color: #fff;
background-color: #ccc;
font-size: 80px;
}
h2 {
margin: 10px 0;
}
p {
color: #666;
margin: 0;
}
// 防抖函数
function debounce(func, wait) {
let timeout = null
return function () {
let context = this
let args = arguments
if (timeout) clearTimeout(timeout)
timeout = setTimeout(() => {
func.apply(context, args)
}, wait)
}
}
// test debounce
let num = 1
let content = document.getElementById('content')
function count() {
content.innerHTML = num++
}
content.onmousemove = debounce(count, 300)
// 节流函数
function throttle(func, wait) {
let timeout = null
return function () {
let context = this
let args = arguments
if (!timeout) {
timeout = setTimeout(() => {
timeout = null
func.apply(context, args)
}, wait)
}
}
}
// test throttle
let num2 = 1
let content2 = document.getElementById('content2')
function count2() {
content2.innerHTML = num2++
}
content2.onmousemove = throttle(count2, 300)