一、防抖
当事件触发时,延迟一段时间后才能再次执行事件处理函数,如果这段时间内事件被重新触发,那么重新开始计时
1、防抖函数的实现原理
- 当事件第一次触发时,设置一个定时器,延迟指定的时间后执行处理函数。
- 如果在延迟时间内事件再次被触发,则清除之前的定时器,重新设置一个新的定时器。
- 只有当事件在延迟时间内没有再次被触发时,处理函数才会执行。
防抖函数是闭包的应用,看一个防抖函数的实现例子
不了解闭包的话,跳转到另一篇文章学习一下深入理解 JavaScript 中的上下文、作用域与闭包
function debounce(fun,delay){
let timer = null
return function(...args){
if(timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fun.apply(this,args)
},delay)
}
}
const handleClick = debounce(() => {
console.log("被点击了")
},300)
btn.addEventListener('click', handleClick);
2、事件触发时函数的执行情况
假设按钮连续点击:
第一次触发 click 事件:
handleClick(...args)被调用timer为null,跳过clearTimeout- 设置新的定时器:
timer = setTimeout(..., 300) - 300ms 后执行:
fun.apply(this, args)→ 输出"被点击了"
在 300ms 内第二次触发:
handleClick(...args)再次被调用timer有值,执行clearTimeout(timer)→ 取消之前的定时器- 设置新的定时器:
timer = setTimeout(..., 300) - 重新开始 300ms 计时
在 300ms 内第三次触发:
- 重复上述过程,再次取消并重新计时
最后一次触发后 300ms 内没有新事件:
- 定时器到期,执行
fun.apply(this, args) - 输出"被点击了"
3、fun.apply(this,args),为什么必须要绑定this
涉及到了this绑定,不了解的点击一文搞懂call,apply,bind三者区别、实现原理以及应用场景学习
在 JavaScript 中,函数的 this 值由调用方式决定,而不是定义位置。
handleClick的this是指向btn的,因为它是由btn调用的,但是fun是直接被使用的,如果不使用 apply,fun 在被直接调用时,其 this 值不会自动绑定到事件处理函数的 this(即 btn),而是使用默认绑定(严格模式下为 undefined)也就是undefined.fun()。使用 fun.apply(this, args) 是为了显式地将外层函数的 this 和参数传递给 fun,使得fun能够正确获取DOM元素上的属性和方法。
二、节流
节流的思想是:在一定时间内只允许事件处理函数执行一次,无论事件触发了多少次。
1、节流函数的实现原理
- 设置一个时间间隔(例如100ms)。
- 当事件第一次触发时,立即执行处理函数,并记录当前时间。
- 在后续事件触发时,检查当前时间与上次执行时间的间隔是否大于设定的时间间隔,如果是,则再次执行处理函数并更新时间记录。
同样的是闭包的应用
function throttle(fun,limit){
let lastTime = null
return function(...args){
let nowTime = new Date().getTime()
if(nowTime - lastTime > limit) {
func.apply(this, args)
lastTime = now
}
}
}
const handleScroll = throttle(() => { console.log('页面滚动了') }, 100)
window.addEventListener('scroll', handleScroll)
三、防抖与节流的区别
- 防抖确保事件在最后一次触发时才执行处理函数,适用于需要在事件结束后才执行的场景,减少不必要的操作和网络请求,提高性能,但是也会因延迟操作,会导致有些用户感知上的延迟。
- 节流在固定的时间间隔内只执行一次处理函数。适合那些需要在事件频繁触发时控制执行频率的场景(如鼠标移动、滚动事件)。可以保证响应频率的同时减少函数的执行次数,提高性能。