概念理解
| 函数 | 基本概念 | 特点 |
|---|---|---|
| 防抖 | 在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时 | 事件触发一段时间之后并且期间没有再次触发才执行 |
| 节流 | 在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效 | 稀释函数执行频率 |
正如参考链接中《7分钟理解JS的节流、防抖及使用场景》一文的比喻:
函数防抖就是法师释放技能的时候要读条,技能读条没完再按技能就会重新读条。
函数节流就是fps游戏的射速,就算一直按着鼠标射击,也只会在规定射速内射出子弹。
应用
防抖
- 在输入框中输入内容后再发起请求,而不是每次输入都发起,例如:搜索触发联想词
- 浏览器窗口大小改变时会频繁触发 resize 事件,用防抖来让其结束改变时只触发一次
节流
可以将一些事件降低触发频率
- 滚动时页面上有些交互效果,此时需要监听scroll事件,但不必每次滑动都触发,可以降低计算的频率,而不必去浪费资源,例如:懒加载时要监听计算滚动条的位置
- 商品预览图的放大镜效果时,不必每次鼠标移动都计算位置
代码
理解了这些含义之后,代码写起来会感觉简单许多。
// 防抖
function debounce(fn, delay=1000) {
let timer // 定时任务
return function() {
const context = this // 谁调用 this 就是谁
const args = [...arguments] // 谁调用就是谁传入的参数
if (timer) clearTimeout(timer) // 如果已有则直接取消然后重新设置
timer = setTimeout(() => {
fn.apply(this, args) // 通过 apply 将上下文和参数都传进去
}, delay)
}
}
// 节流 定时器版,会计算后再执行,最后结束也会执行一次
function throttleByTimeOut(fn, interval=1000) {
let timer = null
return function() {
const context = this
const args = [...arguments]
if (!timer) {
timer = setTimeout(() => {
fn.apply(context, args)
timer = null // 关键操作
}, interval);
}
}
}
// 节流 时间戳版,会先执行一次
function throttleByTimeStamp(fn, interval=1000) {
let prev = new Date().getTime()
return function() {
const context = this
const args = [...arguments]
let now = new Date().getTime()
if (now - prev > interval) { // 计算时间间隔
fn.apply(context, args)
prev = new Date().getTime() // 关键操作
}
}
}