一、引入
为什么需要防抖和节流
我们现在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>
