1. 来源(为什么需要防抖与节流)
在一些高频率事件触发的场景下,我们不希望对应的事件处理函数多次执行
2. 场景
- 滚动事件
- 输入的模糊匹配
- 轮播图切换
- 点击操作
3. 前期储备
浏览器默认情况下会有自己的监听事件间隔(4-6ms),如果监测到多次事件的监听执行,那么就会造成不必要的资源浪费
4. 前置场景
界面上有一个按钮,我们可以连续多次点击
5. 防抖与节流(概念描述)
- 防抖:对于这个高频的操作来说,我们只希望识别一次,可以认为是第一次或最后一次
- 节流:对于高频操作,我们可以自己来设置频率, 让本来会执行很多次的事件触发,按着我们定义的频率减少触发的次数
6. 防抖(代码)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<button id="btn">Add</button>
<script type="text/javascript">
var btn = document.getElementById('btn')
function btnClick(...args) {
console.log('打印了', this, args)
}
function debounce(handle, wait, immediate) {
if(typeof handle === 'undefined') throw new Error('handle must be a function')
if(typeof wait === 'undefined') {
wait = 300
immediate = false
}
if(typeof wait === 'boolean') {
wait = 300
immediate = wait
}
if(typeof immediate !== 'boolean') immediate = false
let timer = null
return function Proxy(...args) {
clearTimeout(timer)
let init = immediate && !timer
timer = setTimeout(() => {
timer = null
!immediate ? handle.call(this, ...args) : null
}, wait)
init ? handle.call(this, ...args) : null
}
}
// btn.onclick = btnClick
btn.onclick = debounce(btnClick, 500, true)
</script>
</body>
</html>
7. 节流(代码)
<!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>节流函数实现</title>
<style>
body {
height: 5000px;
}
</style>
</head>
<body>
<script>
// 节流:我们这里的节流指的就是在自定义的一段时间内让事件进行触发
function myThrottle(handle, wait) {
if (typeof handle !== 'function') throw new Error('handle must be an function')
if (typeof wait === 'undefined') wait = 400
let previous = 0 // 定义变量记录上一次执行时的时间
let timer = null // 用它来管理定时器
return function proxy(...args) {
let now = new Date() // 定义变量记录当前次执行的时刻时间点
let self = this
let interval = wait - (now - previous)
if (interval <= 0) {
// 此时就说明是一个非高频次操作,可以执行 handle
clearTimeout(timer)
timer = null
handle.call(self, ...args)
previous = new Date()
} else if (!timer) {
// 当我们发现当前系统中有一个定时器了,就意味着我们不需要再开启定时器
// 此时就说明这次的操作发生在了我们定义的频次时间范围内,那就不应该执行 handle
// 这个时候我们就可以自定义一个定时器,让 handle 在 interval 之后去执行
timer = setTimeout(() => {
clearTimeout(timer) // 这个操作只是将系统中的定时器清除了,但是 timer 中的值还在
timer = null
handle.call(self, ...args)
previous = new Date()
}, interval)
}
}
}
// 定义滚动事件监听
function scrollFn() {
console.log('滚动了')
}
// window.onscroll = scrollFn
window.onscroll = myThrottle(scrollFn, 600)
</script>
</body>
</html>