预备知识
剩余参数、默认参数、定时器、箭头函数this指向、apply函数、Date对象…
防抖
持续触发事件,n秒内再次触发某事件,将重新计算时间才执行。因此,只有最后一次操作能被触发。
function debounce(fn, delay = 500) {
let timer = null
return (...args) => {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this, args)
}, delay)
}
}
节流
持续触发事件,n秒内再次触发某事件,将等待上一次事件处理函数完成后,才执行。可以把这个理解成游戏中的“技能冷却时间”。
思想碰撞
定时器实现
function throttle(fn, delay = 1500) {
let timer = null
return (...args) => {
if (timer) {
return
}
timer = setTimeout(() => {
fn.apply(this, args)
timer = null
}, delay)
}
}
存在的问题
第一次触发时,也将等待delay 时间才能执行。与冷却时间 的初衷不符合!
定时器优化(个人觉得最佳方法,文尾有解释)
使用first 记录是否是第一次触发函数。如果是第一次触发,立即执行。如果不是,则将采用定时器。
function throttle(fn, delay = 1500) {
let timer = null
let first = true
return (...args) => {
if (timer) {
return
}
if (first) {
fn.apply(this.args)
first = false
return
}
timer = setTimeout(() => {
fn.apply(this, args)
timer = null
}, delay)
}
}
优化后解决的问题
若第一次触发,将立即执行,不会有任何延迟。
时间戳实现
function throttle(fn, delay = 1500) {
let pre = +new Date()
return (...args) => {
const now = +new Date()
if ((now - pre) >= delay) {
fn.apply(this, args)
pre = +new Date()
}
}
}
存在的问题
- 第一次触发时,也将等待
delay 时间才能执行。同样与冷却时间 的初衷不符合! - 最后一次触发时,若
now - pre < delay 将不会执行。 - 使用
Date 对象获取时间戳,计算时间差的办法,会有一个安全漏洞—更改本地时间会出错,因此不推荐!
时间戳实现优化(时间戳+定时器)
function throttle(fn, delay = 1500) {
let timer = null
let pre = +new Date()
return (...args) => {
const now = +new Date()
const remaining = now - pre
clearTimeout(timer)
if(remaining > delay) {
fn.apply(this, args)
pre = +new Date()
}else {
timer = setTimeout(() => {
fn.apply(this, args)
}, delay)
}
}
}
优化后解决的问题
最后一次触发,若now - pre < delay 也将会执行。
优化后仍然存在的问题
- 第一次触发,会有延迟,依旧需要等待
delay 时间差才能执行。 - 修改本地时间,任会出现错误。
总结
个人觉得使用定时器优化 方案是最佳方案。优点如下:
- 第一次执行将立即执行,不会有任何延迟。
- 最后一次执行,也将会触发执行。
- 不存在
更改本地时间 的BUG。
网友们觉得呢?欢迎评论区讨论~
|