前言#
本文介绍函数节流 throttle 和 函数防抖 debounce 的实现方法。
函数节流 throttle#
函数节流就是高频触发事件,指定时间内只执行一次。
我们来想象一下需要函数节流的场景,比如典型的监听页面滚动触发回调函数,我们写一段测试代码,然后来看看多次触发的情况:
<style>
div {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.wrap {
position: absolute;
top: 50%;
left: 50%;
height: 200px;
width: 200px;
padding: 0 10px;
transform: translate(-50%, -50%);
border: 1px solid black;
overflow: auto;
}
.content {
width: 100%;
height: 500px;
background: lightblue;
}
</style>
<div class="wrap">
<div class="content"></div>
</div>
<script>
var wrap = document.querySelector('.wrap')
var content = document.querySelector('.content')
wrap.addEventListener('scroll', function () {
console.log('dispatch')
})
</script>html我们为 wrap 绑定了一个滚动事件的监听,这种需求是经常用到的,比如我们需要根据用户的滚动显示不同的内容,但是很明显的是我们并不需要如此频繁地执行回调函数,我们需要让这个监听的回调函数执行的次数少一点,这时候就需要用到函数节流。
浏览器的监控肯定是固定频率持续发生的,这是我们无法控制的,我们能做的就是在执行流进入到回调函数的时候进行判断,如果不符合条件我们就不执行,这就是函数节流的原理。如何设计这个逻辑呢,我们可以利用 JavaScript 的定时器配合一个表示状态的锁来达到目的,代码如下:
wrap.addEventListener(
'scroll',
throttle(function () {
console.log('dispatch: ' + new Date().getTime())
}, 300)
)
function throttle(fn, interval) {
var executing = false
return function () {
if (executing) return
executing = true
setTimeout(() => {
fn.apply(this, arguments)
executing = false
}, interval)
}
}javascript现在我们的回调函数在 executing 为 true 的时候会直接返回,而我们的功能代码也就是 fn 每隔 300ms 才会执行一次,效果如下:
需要注意的是
setTimeout的回调函数中的this默认指向window,因为setTimeout是window对象上的一个方法,调用setTimeout实际上是window.setTimeout,而我们的fn中的this需要指向绑定事件的DOM元素,所以需要bind,箭头函数或者中间变量。这里我使用的箭头函数,用apply来设置函数执行的this和参数。
函数防抖 debounce#
防抖 debounce 的概念其实是从机械开关和继电器的“去弹跳”(debounce)衍生出来的,基本思路就是把多个信号合并为一个信号。
编程中的函数防抖就是在事件被触发 n 秒后再执行回调,如果在这 n 秒内又被触发,则重新计时,也就是把一些高频触发事件的多次触发信号合并成一个,达到 debounce 的效果。主要针对的是回调只需要执行一次,但是事件却频繁触发,如果每次触发我们都执行回调,会造成性能的浪费。这在生活中也很常见,比如电梯门打开后默认 3s 后关闭,如果在这段时间中有人又按了按钮,会从头开始计算 3s。
这里我们用一个最常见的例子来说明,用户修改输入框输入后向后台发请求,如果用户每次修改输入我们都发请求,那么是很浪费的。我们可以设置一个 500ms 的延迟,500ms 内用户又进行了输入则重新计时,如果 500ms 内用户都没有输入则发送请求。
主要的实现思路就是用 setTimeout 和 clearTimeout,我们将待执行函数用一个 debounce 函数进行包装。当事件触发的时候我们首先进行 clearTimeout 删除之前设置的定时器,然后重新用 setTimeout 设置一个定时器。只有当事件在指定时间内没有触发了,setTimeout 的回调函数才会执行。
<!DOCTYPE html>
<html>
<head>
<title>debounce</title>
</head>
<body>
<input id="inp" type="text" />
<script>
function debounce(fn, interval) {
let timeout = null
return function () {
clearTimeout(timeout)
timeout = setTimeout(() => {
fn.apply(this, arguments)
}, interval)
}
}
function sayHi() {
console.log('debounce success!')
}
var inp = document.getElementById('inp')
inp.addEventListener('input', debounce(sayHi, 500)) // 防抖
</script>
</body>
</html>html总结#
函数节流和函数防抖主要是应用在一些高频触发事件上,当我们的事件不需要那么高的触发频率的时候,可以用节流或者防抖处理。
想要查看文章中的示例请点击 ↗