一、引入
为什么需要防抖和节流
我们现在JS主要是在浏览器下运行,涉及到很多人机交互的操作。假设一个场景,我们打开的网页有一个轮播图,我们对左右切换的按钮疯狂点击。按钮是绑定了操作的,那么每次我们点击这个按钮被监听到后就会执行代码,有部分内存会被使用,性能也有所消耗。这就是所谓的高频次触发的场景。在这种场景下,我们希望事件对应的监听不需要立即或者说反复被触发,这样我们就需要对其进行防抖和节流的操作。
使用场景
- 滚动事件
- 输入的模糊匹配
- 轮播图切换
- 点击操作
- ……
浏览器默认情况下都会有自己的监听事件间隔,比如Chrome浏览器监听间隔是4-6ms。如果检测到多次事件的监听执行,那么就会造成不必要的资源浪费。
概念
防抖:对于高频操作来说,我们只希望只识别一次,可以认为认为是第一次或者最后一次。
节流:对于高频操作,我们可以自己设置频率,让本来会执行很多次的事件触发,按照我们定义的频率减少触发次数。
二、防抖函数实现
场景
有一个按钮,我们对其进行高频触发,但是希望只识别一次。
原始代码
这个代码下,就是点击一次执行一次。
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>防抖函数实现</title>
<body>
<button id="btn" >点击</button>
<script>
//找到目标元素
var oBtn = document.getElementById('btn')
oBtn.onclick = function () {
console.log('点击了')
}
</script>
</body>
</html>
防抖函数代码
里面具体的思路和每一块是干什么的,都写在代码注释里面了,这边就不展开了。
-
-
<html>
-
<head>
-
<meta charset="UTF-8">
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
-
<title>防抖函数实现</title>
-
</head>
-
<body>
-
<button id="btn" >点击</button>
-
<script>
-
//找到目标元素
-
var oBtn = document.getElementById('btn')
-
// oBtn.onclick = function () {
-
// console.log('点击了')
-
// }
-
/**
-
* handle 要执行的操作
-
* wait 事件触发后多久开始执行
-
* immediate 控制执行第一次还是最后一次,如果是false就执行最后一次,如果是true就执行第一次
-
*/
-
// 其实这些参数我们可以考虑给他们添加默认值比如wait = 300, immediate = false。
-
// 但是定义一个函数给它的形参赋予一个默认值,这样的话它整个函数体里的代码将来如果出现一些变量定义之后,会有一些比较变态的机制.这边不做讨论,所以先不设置默认值了
-
function myDebounce (handle,wait,immediate) {
-
//参数类型判断及默认值处理
-
if(typeof handle !== 'function') throw new Error('handle must be a function')
-
if(typeof wait === 'undefined') wait = 300
-
if(typeof wait === 'boolean') {
-
immediate = wait
-
wait = 300
-
}
-
if(typeof immediate === 'undefined') immediate = false
-
-
// 所谓的防抖效果,我们想要实现的是有一个”人“,可以管理handle的执行次数
-
// 如果我们想要执行最后一次,那意味着无论我们当前点击了多少次,前面的N-1次都没有用
-
let timer = null
-
return function proxy(...args) {
-
let self = this
-
init = immediate && !timer
-
clearTimeout(timer)
-
timer = setTimeout(() => {
-
timer = null
-
//只有我们的immediate为false才执行下面这个代码,否则就不执行【原本其实就是handle(),但是为了拿到this和mouseEvent所以改成下面这个样子】
-
!immediate ? handle.call(self, ...args) : null
-
},wait)
-
//如果当前传进来的是true,就表示我们需要立即执行
-
// 如果想要实现只在第一次执行,可以添加上timer为null作为判断,因为只要timer为null,就意味着没有第二次
-
init ? handle.call(self, ...args) : null
-
}
-
}
-
// 定义事件执行函数,这里的ev是点击的MouseEvent,this是button元素
-
function btnClick(ev) {
-
console.log('点击了', this, ev)
-
}
-
// 当我们执行了按钮点击之后就会执行防抖函数返回的proxy
-
//oBtn.onclick = myDebounce(btnClick,false)
-
oBtn.onclick = myDebounce(btnClick,200,true)
-
</script>
-
</body>
-
</html>
三、节流函数实现
场景
一个页面我们往下滚动,触发相应事件。
原始代码
这个代码下,我们只要做出滚动的动作就会被监听到,不断触发scrollFn方法。但是这其实是会造成资源浪费的,我们并不需要这么高频次的响应触发。
-
-
<html>
-
<head>
-
<meta charset="UTF-8">
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
-
<title>节流函数实现</title>
-
<style>
-
body {
-
height:5000px
-
}
-
</style>
-
</head>
-
<body>
-
<script>
-
// 定义滚动事件监听
-
function scrollFn() {
-
console.log('滚动了')
-
}
-
window.onscroll = scrollFn
-
</script>
-
</body>
-
</html>
节流函数代码
我们这里的节流指的是在自定义的一段事件内让事件进行触发
-
-
<html>
-
<head>
-
<meta charset="UTF-8">
-
<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 a function')
-
if(typeof wait === 'undefined') wait = 400
-
-
let previous = 0 // 定义变量记录上一次执行的时间
-
let timer = null // 用来管理定时器
-
return function proxy(...args) {
-
let self = this
-
let now = new Date()
-
let interval = wait - ( now - previous)
-
if(interval <= 0){
-
//万一很巧的定时器延迟到的操作和我们的点击同时进行
-
clearTimeout(timer)
-
timer = null
-
//非高频次操作,可以执行handle
-
handle.call(self, ...args)
-
previous = new Date()
-
}
-
//当我们发现系统中有一个定时器了,就不需要再开启定时器了
-
else if(!timer){
-
//这次操作发生在我们定义的频次时间范围内,不应该执行
-
// 这个时候可以自己定义一个定时器,让handle在interval之后去执行
-
timer = setTimeout(() => {
-
clearTimeout(timer)// 这个操作知识将系统中的定时器清楚了,但是timer中的值还在,所以需要手动吧timer = null
-
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>