前言
当某个事件在短时间内被高频触发的时候,比如用户短时间多次点赞取消点赞、输入框查询数据、监听页面滚动等,触发的频率非常高,会对服务器或者浏览器性能造成压力,如果碰到这些问题,那就需要考虑用到节流和防抖了.
节流(throttle)
定义:在规定时间内,只让函数触发的第一次生效,后面不生效,超过规定时间才会执行第二次
实现原理
执行js函数,然后记录上次执行时间戳,再次执行js函数的时候判断一下当前时间戳距离上次时间戳是否超过规定时间,如果是则执行,并更新上次时间戳,如此循环:
function throttle(fn, delay) {
// 记录上次执行时间戳
let lastTime = 0
return function () {
// 记录当前函数触发的时间
let nowTime = Date.now()
if (nowTime - lastTime > delay) {
// 修改this的指向问题
fn.call(this)
// 更新时间戳
lastTime = nowTime
}
}
}
document.onscroll = throttle(function() { console.log('scroll事件被触发了' + Date.now()) }, 200)

上面例子中使用了闭包的特性,可以使变量lastTime的值长期保存在内存中.
应用场景
- 多次点击提交、点赞取消点赞频繁切换
- DOM元素的拖拽功能实现
- 计算鼠标移动的距离
- Canvas 模拟画板功能
- 射击游戏的 mousedown/keydown 事件(单位时间只能发射一颗子弹)
- 监听滚动事件判断是否到页面底部自动加载更多:给 scroll 加了 debounce 后,只有用户停止滚动后,才会判断是否到了页面底部;如果是 throttle 的话,只要页面滚动就会间隔一段时间判断一次
防抖
定义:在规定时间内,只让函数触发的最后一次生效,前面不生效
实现原理
第一次调用函数,创建一个定时器,在指定的时间间隔之后运行代码,在规定时间内多次触发这个方法,后一次调用函数的时候都会先清除上一次的定时器并新建一个,然后在规定时间内运行.
function debounce(fn, delay) {
// 记录上一次的延时器
var timer = null;
return function() {
// 清除上一次延时器
clearTimeout(timer)
timer = setTimeout(function() {
fn.apply(this)
}, delay)
}
}
document.getElementById('btn').onclick = debounce(function() {
console.log('点击事件被触发' + Date.now())
}, 1000)

上例中也用到了闭包的特性,可以使变量timer的值长期保存在内存中.
应用场景
- 每次 resize/scroll 触发统计事件
- 文本输入的验证(连续输入文字后发送 AJAX 请求进行验证,验证一次就好)
- 输入框输入查询数据
总结:节流和防抖的核心其实就是限制某一个方法被频繁触发,节流是执行第一次,超过规定时间执行第二次,防抖是在规定时间执行最后一次
网友评论