基本概念
防抖和节流都是用来优化,防止时间触发频率过高导致的响应速度跟不上触发频率,出现延迟、假死或卡顿的现象
防抖(debounce):触发事件后一段时间内内函数只会执行一次,如果在这段时间事件再次被触发,则重新计算时间
节流(throttle):事件被触发,但在一段时间内只会执行一次,所以节流会稀释函数的执行频率
以滚动监听条为例,详细讲解一下防抖和节流
function showTop () {
let scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
console.log('滚动条位置:' + scrollTop);
}
window.onscroll = showTop
一、防抖
- 实现:借用setTimeout函数设置延迟调用,每当用户再次调用则取消前一次调用
- 缺点:事件如果不断被触发,调用方法也会迟迟不能出来
- 应用场景:联想搜索、收藏点击、window触发resize等
function debounce(func, delay=100){
// 初始化计时器
let timer = 0
return function (...args) {
// 如果再次调用,则清除计时,重新调用
if(timer) clearTimeout(timer)
timer = setTimeout(() => {
// 调用方法
func.apply(this, args)
}, delay)
}
}
window.onscroll = debounce(showTop, 1000)
以上为基础的防抖,因为它通过异步函数最后执行,有一定延迟;一般的防抖会有immediate选项,表示是否立即调用
- 延迟调用:搜索框input事件,支持输入实时搜索可以使用节流方案(间隔一段时间就必须查询相关内容----例如某度)。输入间隔大于某个值(如500ms),就当做用户输入完成,然后开始搜索
- 立即执行:用户点收藏、star的时候,我们希望用户点第一下的时候就去调用方法,并且成功之后改变收藏、star按钮的样子,用户就可以立马得到反馈是否star成功了
因此,我们升级一下防抖函数:
function debounce(func, delay=100, immediate=true){
let timer, context, args
// 延迟执行函数
const later = () => setTimeout(() => {
// 延迟函数执行完毕,清空定时器
timer = null
if(!immediate){
func.apply(context, args)
context = args = null
}
}, delay)
return function(...params){
// 初次调用,创建later
if(!timer){
timer = later()
// 如果立即执行
if(immediate){
func.apply(this, params)
} else {
context = this
args = params
}
}
// 如果再次调用,则清除计时,重新调用
else{
clearTimeout(timer)
timer = later()
}
}
}
二、节流
- 实现:一种是借用setTimeout函数,另一种是判断前后调用时间差,进阶版是将以上两种糅合在一起。每当用户触发事件,先判断函数是否已经调用,如果调用则直接截断退出
- 应用场景:游戏功能(如王者荣耀技能按钮)、滚动条监听等
- 首先是时间戳基础版
function throttle(func, wait){
// 初始化时间戳
let context, args, prev = Date.now()
return function (...parmas) {
// 函数执行时间戳
let _now = Date.now()
// 如果时间差不足等待时间,则截断退出
if(_now - prev < wait) return false
// 时间差大于等待时间,函数可重新调用
context = this
args = parmas
func.apply(context, args)
prev = Date.now() //更新上一次函数执行时间戳
}
}
window.onscroll = throttle(showTop,1000)
- 接下来是setTimeout基础版
function throttle(func, delay){
// 标记函数是否调用过
let valid = true
return function(...args){
// 函数正在执行,直接退出调用
if(!valid) return false
// 函数进入工作,不可再次执行
valid = false
setTimeout(() => {
func.apply(this, args)
valid = true //执行完毕
}, delay)
}
}
window.onscroll = throttle(showTop,1000)
同样也是延迟的问题,游戏玩家点击技能,总不能让技能等一段时间才发出,因此我们需要加入immediate选项(暂未完成)