防抖与节流
什么情况下需要防抖与节流:
在一些高频率事件触发的场景下,不希望对应的事件处理函数多次执行
比如:
滚动事件
input输入框的模糊查询
轮播图切换
点击操作等
浏览器默认情况下都会有自己的监听事件间隔(谷歌浏览器 4 - 6 ms), 如果检测到多次,就会造成不必要的资源浪费
防抖:对于高频操作来说,我们只希望识别一次点击,即只触发一次对应的事件,第一次或最后一次都可以
节流:对于高频操作,可以自行设定事件触发频率,以使多次触发的事件,在规定时间内,只触发一次
防抖实现:
<button id="btn">点击按钮</button>
<script>
let btn = document.getElementById("btn")
/**
* handle 最终要执行的事件监听
* wait 事件触发多久后开始执行
* immediate 控制执行第一次还是最后一次, false执行最后一次,true执行第一次
*/
function debounce(handler, wait, immediate){
//首先对传入的参数进行校验
if(typeof handler !== 'function'){ throw new Error('handler must be a function')}
if(typeof wait === 'undefined') wait = 300
if(typeof wait === 'boolean') {
immediate = wait
wait = 300
}
let timer = null
return function proxy(){
if(timer){
clearTimeout(timer)
timer = null
}
timer = setTimeout(() => {
handler()
}, wait)
}
}
function btnClick(a,b){
console.log('点击了123333')
}
btn.onclick = debounce(btnClick,false)
</script>
给wait设定默认值300ms, 在不传wait的情况下默认 300ms内只会触发一次,传值的话就会在指定时间内只触发一次
如果btnClick会有参数传入,如何处理?this如何处理?
改造一下代码
<button id="btn">点击按钮</button>
<script>
let btn = document.getElementById("btn")
/**
* handle 最终要执行的事件监听
* wait 事件触发多久后开始执行
* immediate 控制执行第一次还是最后一次, false执行最后一次,true执行第一次
*/
function debounce(handler, wait, immediate){
//首先对传入的参数进行校验
if(typeof handler !== 'function'){ throw new Error('handler must be a function')}
if(typeof wait === 'undefined') wait = 300
if(typeof wait === 'boolean') {
immediate = wait
wait = 300
}
let timer = null
return function proxy(...args){
//定义变量self接收this
let self = this
if(timer){
clearTimeout(timer)
timer = null
}
timer = setTimeout(() => {
handler.call(self,...args)
}, wait)
}
}
function btnClick(a,b){
//返回一个匿名函数
return function fn(){
console.log('点击了了了了',this, a, b)
}
}
btn.onclick = debounce(btnClick(1,2),false)
</script>
在我们的debounce函数中,immediate用来控制,是否只执行第一次,如何是immediate参数生效?
改造一下代码:
<button id="btn">点击按钮</button>
<script>
let btn = document.getElementById("btn")
/**
* handle 最终要执行的事件监听
* wait 事件触发多久后开始执行
* immediate 控制执行第一次还是最后一次, false执行最后一次,true执行第一次
*/
function debounce(handler, wait, immediate){
//首先对传入的参数进行校验
if(typeof handler !== 'function'){ throw new Error('handler must be a function')}
if(typeof wait === 'undefined') wait = 300
if(typeof wait === 'boolean') {
immediate = wait
wait = 300
}
if(typeof immediate !== 'boolean') immediate = false
let timer = null
return function proxy(...args){
let self = this,
init = immediate && !timer //定义一个变量,控制是否只执行第一次
if(timer){
clearTimeout(timer)
timer = null
}
timer = setTimeout(() => {
timer = null
//如果immediate为true则不会触发handler
!immediate ? handler.call(self,...args) : null
}, wait)
//timer为null 且 传入的immediate为true
init ? handler.call(self, ...args) : null
}
}
function btnClick(a,b){
return function fn(){
console.log('点击了了了了',this)
}
}
btn.onclick = debounce(btnClick(),300, true)
</script>
节流实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body{
height: 5500px;
}
</style>
</head>
<body>
<script>
function throttle(handler, wait){
if(typeof handler !== 'function') throw new Error("handler must be a function")
if(typeof wait === 'undefined') wait = 400 //给wait默认值400ms
let previous = 0 //定义变量记录上一次执行的时间
let timer = null //管理定时器
return function proxy(...args){
let now = new Date()
let self = this
let interval = wait - (now - previous)
//interval<= 0 说明是非高频次操作,可以执行handle
if(interval <= 0){
clearTimeout(timer)
timer = null
handler.call(self, ...args)
previous = new Date()
}else if(!timer){
// 另一种情况是此时interval > 0 我们就讲下次执行结果存在定时器中, 且不可能一直去存储,只有 timer = null时采取存储
timer = setTimeout(() => {
//清掉上一个定时器
clearTimeout(timer) //这个操作只是吧系统中定时器清除掉了,但是timer中的值还在,因此需要在下面将timer置位null
timer = null
//执行操作
handler.call(self, ...args)
previous = new Date()
},interval)
}
}
}
function scroll(){
console.log("滚动了")
}
window.onscroll = throttle(scroll, 600)
</script>
</body>
</html>