1. 防抖节流 的 异同
相同 :
- 防抖和节流 是
高频触发事件,频繁回调函数时做的 性能优化
,以节约请求资源,避免大量计算导致的页面卡顿/浏览器卡死;
不同 :
- 防抖是将多次执行变为最后一次执行 – 频繁触发时,规定时间内,回调函数只能执行一次;
- 节流是将多次执行变为在
规定时间内只执行一次
– 节流会稀释函数的执行频率
防抖是控制次数,节流是控制频率
2. 防抖(debounce) 实现
非立即执行版 - 延时防抖
延时防抖的表现为:最后一次触发后间隔T后再调用函数
场景 :
输入搜索联想
思路:
- 在第一次触发事件时,不立即执行函数,用setTimeout 延后n秒执行
- 如果n秒内再次触发滚动事件,那么取消当前计时器,重新开始计时
- 如果n秒内没有再次触发滚动事件,那么就执行函数
/*
* fn [function] 需要防抖的函数
* delay [number] 毫秒,防抖期限值
*/
function debounce(fn,delay){
let timer = null //借助闭包
return function() {
if(timer){
clearTimeout(timer) //进入该分支语句,说明当前正在一个计时过程中,并且又触发了相同事件。所以要取消当前的计时,重新开始计时
timer = setTimeOut(fn,delay)
}else{
timer = setTimeOut(fn,delay) // 进入该分支说明当前并没有在计时,那么就开始一个计时
}
}
}
简化:
function debounce(fn,delay){
let timer = null //借助闭包
return function() {
if(timer){
clearTimeout(timer)
}
timer = setTimeout(fn,delay) // 简化写法
}
}
防抖的问题 :
- 如果在限定时间段内,不断触发滚动事件,只要不停止触发,理论上就永远不会触发回调函数
立即执行版
场景 :
按钮点击:收藏,点赞,心标等立即反馈的按钮,或者用于对于按钮防点击
思路:
- 在第一次触发事件时,立即执行函数,然后创建一个定时器用setTimeout 延后n秒清除定时器
- 如果n秒内再次触发事件,那么取消当前计时器,重新开始计时/延时
- 如果n秒内没有再次触发滚动事件,延时结束后,且并不会执行回调函数
function debounce(fn,delay){
let timer = null //借助闭包
return function() {
fn(); // 1.第一次进入立即执行
if(timer){
clearTimeout(timer); //2. 有计时器,清除计时器
}
timer = setTimeout(()=>{ // 3. 重新创建一个定时器,n秒后清除定时器
timer = null;
},delay) // 简化写法
}
}
3. 节流(throttle) 实现
定时器版
用setTimeout + 开关 实现
思路:
- 默认开关打开,在第一次触发事件时,关掉开关 + 用setTimeout 延后n秒执行
- 再次触发滚动事件,判断开关状态,关–退出函数,开的话-- 关掉开关 + 用setTimeout 延后n秒执行
- 如果n秒内没有再次触发滚动事件,那么就执行函数,打开开关
function throttle(fn,delay){
let canRun = true
return function() {
if(!canRun){
//休息时间 暂不接客
return false
}
// 工作时间,执行函数并且在间隔期内把状态位设为无效
canRun = false
setTimeout(() => {
fn()
canRun = true;
}, delay)
}
}
/* 请注意,节流函数并不止上面这种实现方案,
例如可以完全不借助setTimeout,可以把状态位换成时间戳,然后利用时间戳差值是否大于指定间隔时间来做判定。
也可以直接将setTimeout的返回的标记当做判断条件-判断当前定时器是否存在,如果存在表示还在冷却,并且在执行fn之后消除定时器表示激活,原理都一样
*/
// 以下照旧
window.onscroll = throttle(showTop,1000)
时间戳版
function throttle(fn, delay) {
let previous = 0;
return function() {
let now = Date.now();
if (now - previous > delay) {
fn.apply(this, arguments);
previous = now;
}
}
}
补充:
setTimeout里 fn()
可以换为 fn.apply(this , arguments);
来解决this 指向 和 传参问题
参考:
浅谈js防抖和节流
什么是防抖和节流?有什么区别?如何实现?
函数防抖和节流
前端面试查漏补缺–(一) 防抖和节流
延时执行、立即执行防抖的不同实现